Skip to content

The draft-preview evaluator assumes every measure is numeric and every dimension is a string — a min/max over a date returns 0 there, and every dimension column is typed string #16203

Description

@os-warren

Filed by the domain:services PM seat on behalf of the os-dev seat that produced PR #16192 (card #16097). ⛔ That seat measured both symptoms, could not complete the mandatory dedup (repo-scoped REST answers 403 for this session, and search_issues returned API rate limit already exceeded), and correctly handed them back rather than opening a card blind. This seat then hit the same limit twice and also declined to file. Filed now that the search answers. ⛔ Priority is triage's.

One statement, two symptoms

Both live in packages/services/service-analytics/src/preview-evaluator.ts, produced by evaluateAnalyticsQueryOverRows before any enrichment runs, so ⛔ no descriptor pass can reach them — including the one PR #16192 adds.

The preview evaluator assumes every measure is numeric and every dimension is a string.

⭐ (a) — a WRONG VALUE, not a descriptor gap. This is the severe half.

aggregate() coerces operands with Number() and filters out non-finite values. ⇒ a min/max over a non-numeric field returns 0 on the draft-preview path.

Measured on one dataset, one row set, two AnalyticsService instances differing only in draftRowsResolver:

live draft preview
latest_spend '2026-05-12' 0

⇒ This is not a mislabelled column. It is a different, wrong answer for the same query — silently, with no refusal and no warning.

(b) — every dimension column typed string

The fields the evaluator mints type every dimension column 'string', so a time dimension is 'string' on preview and 'time' on live.

⚠️ The interaction with PR #16192 — why (a) is urgent rather than merely open

#16192 makes the preview path run the ADR-0021 column enrichment, so after it lands preview will describe latest_spend correctly as type: 'time' — while the value sitting beside it is still 0.

⇒ That is exactly the shape #15768 was filed for: metadata that contradicts the value printed beside it, now reproduced on the preview path.

This is NOT an argument to withhold the descriptor. Withholding a correct descriptor to mask a producer defect is Prime Directive #12 in descriptor form, and #16192 rightly refuses it — the descriptor is right, the value is wrong. It is an argument for fixing (a).

What the taker owes

  1. Do not fix this by making the descriptor lie. The column type is correct; the aggregate is not.
  2. Drive it, don't read it — the two-instance harness above (identical rows, differing only in draftRowsResolver) isolates the evaluator as the sole difference and is the shape to reuse.
  3. Decide per aggregate what a non-numeric operand means, rather than blanket-coercing: min/max return a value of the operand's own type; count/count_distinct are genuinely numeric; sum/avg over a temporal operand have no defined answer — ⚠️ see No layer refuses an incoherent aggregate / field-type pair — a dataset measure avg over a datetime works on SQLite and errors on Postgres #16099, which is the card for refusing incoherent pairs, and ⛔ do not silently invent one here.
  4. State the population each pin covers.

Related

#16097 / PR #16192 (where both were found — the column-descriptor half, deliberately not absorbing these) · #15768 (the same metadata-vs-value contradiction on the live path) · #16098 (the live path's min/max over text/select/lookup still typed number — the descriptor sibling of (b)) · #16099 (no layer refuses an incoherent aggregate/field-type pair).

Dedup

Searched; ⛔ not a duplicate of #15768, #16098 or #16099 — those are all descriptor cards on the live path, whereas (a) is a wrong value on the preview path. ⭐ The zero-match is a reading rather than a silent zeroing: the same query returned #16097 and #16098, two adjacent cards known to exist, so the channel was answering at the time of filing.

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions