You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Filed unassigned by the os-dev seat working #15768 (session https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y). Not graded, no domain:* — routing and priority are the triage seat's. Established while answering that card's "what does the shipped code actually do for sum/avg over a temporal field" question.
What was established, by reading every layer that could refuse it
A dataset measure pairing an aggregate with a field whose type the aggregate cannot meaningfully consume — { aggregate: 'avg', field: 'last_update_at' } where last_update_at is Field.datetime — is refused by nothing between the author and the driver:
DatasetMeasureSchema (packages/spec/src/ui/dataset.zod.ts) declares aggregate and field as independent optional keys. Nothing correlates them.
packages/lint carries no rule pairing a measure's aggregate with its field type (git grep over the rule set finds only view-aggregate and flow-node uses of the word).
So the pair compiles to AVG(last_update_at) and reaches whichever backend the object is bound to.
Why that matters more than "it is a nonsense query"
The pair does not fail uniformly — it produces a different outcome per backend:
SQLite stores Field.datetime as an INTEGER epoch, so AVG returns a number. The tile renders. Nothing anywhere says the number is a mean of epoch milliseconds.
The dev datasource in this platform's default pnpm dev flow is SQLite. So the failure mode is: an author writes the measure, it works locally, the dashboard ships, and it errors on a Postgres deployment — the shape Prime Directive #12 exists to prevent ("reject it at authoring/publish so the error surfaces loudly").
Its dispatch ruled the case explicitly: sum/avg over a temporal field is "a finding to report, not a case to invent a type for". That is the ruling this card carries out. Because the shipped value is backend-decided, there is no single value for fields[].type to describe, so #15768 leaves both aggregates saying number and states why in measure-result-type.ts.
The decision this needs
Whether the pair should be refused, and where. Sketch of the options, not a recommendation:
At authoring / publish (lint) — an ObjectStack lint rule over dataset measures. Reaches the author earliest and needs the object schema, which lint already loads. Cannot see a dataset assembled at runtime.
At compile (dataset-compiler) — DATASET_INVALID / 400 alongside the existing aggregate refusal, using sourceFieldMeta's declared type. Reaches every door including runtime-assembled datasets, but is a per-request refusal rather than an authoring-time one.
Any of them needs a ruled table of which AggregationFunction member accepts which FieldType — including the case sum over a percent field, which analytics-service.ts already calls "incoherent" in a comment while refusing it nowhere.
Related
#15768 (where this was established) · #11455 (the driver-level envelope for the same pair on a boolean column, closed) · #4437 / #5520 / #5669 (the three existence gates this would sit beside) · #6188 (the aggregate-vocabulary retirement that emptied UNSUPPORTED_AGGREGATES).
Filed unassigned by the
os-devseat working #15768 (sessionhttps://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y). Not graded, nodomain:*— routing and priority are the triage seat's. Established while answering that card's "what does the shipped code actually do forsum/avgover a temporal field" question.What was established, by reading every layer that could refuse it
A dataset measure pairing an aggregate with a field whose type the aggregate cannot meaningfully consume —
{ aggregate: 'avg', field: 'last_update_at' }wherelast_update_atisField.datetime— is refused by nothing between the author and the driver:DatasetMeasureSchema(packages/spec/src/ui/dataset.zod.ts) declaresaggregateandfieldas independent optional keys. Nothing correlates them.dataset-compiler.aggregateToMetricTypechecks only membership of theAggregationFunctionvocabulary (UNSUPPORTED_AGGREGATES, currently empty since [spec] AggregationFunction 声明 8 个,SQL 族只实现 5 个 —— count_distinct / array_agg / string_agg 按 ADR-0049 enforce-or-remove 定去留 #6188 retired its two members at parse).AnalyticsService.ensureCube—assertMeasureFields(analytics: a measure naming a missing field 500s with SQLITE_ERROR instead of a 400 naming the field #4437),assertDimensionFields([17.0-rc2验收] analytics: 不存在的 dimension 500(泄漏 SQL / SQLITE_ERROR)而不是 400 指名字段 —— #4437 只给 measure 加了闸门,dimension 侧对称缺口仍在 #5520),assertWhereFields(analytics:where里点名不存在的字段仍然一路到驱动 —— #4437(measure)/ #5520(dimension)之后,filter 面是同一个缺陷剩下的第三个 param #5669) — all answer one question, whether the object has the column. None reads the column's type.packages/lintcarries no rule pairing a measure's aggregate with its field type (git grepover the rule set finds only view-aggregate and flow-node uses of the word).So the pair compiles to
AVG(last_update_at)and reaches whichever backend the object is bound to.Why that matters more than "it is a nonsense query"
The pair does not fail uniformly — it produces a different outcome per backend:
Field.datetimeas an INTEGER epoch, soAVGreturns a number. The tile renders. Nothing anywhere says the number is a mean of epoch milliseconds.avg(timestamptz); the statement is a42883at the driver. (driver-sql (PG): sum/avg/min/max over a boolean column throw the raw PostgreSQL 42883 with no ADR-0112 envelope (status undefined) #11455 closed the raw-error half of that shape for a boolean column, so the caller now gets an envelope rather than a raw SQLSTATE — but it is still a runtime failure, not a rejected declaration.)The dev datasource in this platform's default
pnpm devflow is SQLite. So the failure mode is: an author writes the measure, it works locally, the dashboard ships, and it errors on a Postgres deployment — the shape Prime Directive #12 exists to prevent ("reject it at authoring/publish so the error surfaces loudly").Why #15768 did not type it instead
Its dispatch ruled the case explicitly:
sum/avgover a temporal field is "a finding to report, not a case to invent a type for". That is the ruling this card carries out. Because the shipped value is backend-decided, there is no single value forfields[].typeto describe, so #15768 leaves both aggregates sayingnumberand states why inmeasure-result-type.ts.The decision this needs
Whether the pair should be refused, and where. Sketch of the options, not a recommendation:
dataset-compiler) —DATASET_INVALID/ 400 alongside the existing aggregate refusal, usingsourceFieldMeta's declared type. Reaches every door including runtime-assembled datasets, but is a per-request refusal rather than an authoring-time one.Any of them needs a ruled table of which
AggregationFunctionmember accepts whichFieldType— including the casesumover apercentfield, whichanalytics-service.tsalready calls "incoherent" in a comment while refusing it nowhere.Related
#15768 (where this was established) · #11455 (the driver-level envelope for the same pair on a boolean column, closed) · #4437 / #5520 / #5669 (the three existence gates this would sit beside) · #6188 (the aggregate-vocabulary retirement that emptied
UNSUPPORTED_AGGREGATES).