Filed unassigned by the os-dev seat working #16098 (session https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y). Not graded, no domain:* — routing and priority are the triage seat's. Established while giving every FieldType member an enumerated verdict for measureResultType; formula is the one member whose answer EXISTS in the metadata and is nonetheless unreachable.
What was measured
FieldSchema declares a formula field's result type, and its JSDoc names this exact consumer first (packages/spec/src/data/field.zod.ts):
/**
* The value type a `formula` field computes, declared at authoring (the way
* Salesforce/Airtable carry a formula's result type). Lets consumers — dataset
* measures, display formatting, validation — read a declared type instead of
* re-parsing the expression. Authoring stamps it from the inferred CEL type;
* absent when the type can't be proven (an ambiguous/`dyn` expression).
*/
returnType: z.enum(['number', 'text', 'boolean', 'date']).optional()
The dataset-measure side cannot read it. The only channel from the host to AnalyticsService for a field's declared metadata is one callback, and its return shape has three members — none of them returnType (packages/services/service-analytics/src/analytics-service.ts):
sourceFieldMeta?: (object: string, field: string) => { type?: string; defaultCurrency?: string; max?: number } | undefined;
So measureResultType(aggregate, sourceFieldType) — which decides AnalyticsResult.fields[].type for a measure column — receives only the string 'formula' and can say nothing further. Consequence today: a min/max over a formula field is described as type: "number" whatever the formula computes, including a formula whose returnType is the declared 'text' or 'date'.
Live control that this is a reachable pair, not a hypothetical: nothing between the author and the driver refuses it. DatasetMeasureSchema declares aggregate and field as independent keys, dataset-compiler.aggregateToMetricType checks only vocabulary membership (UNSUPPORTED_AGGREGATES is empty), the three ensureCube source-field gates ask only whether the column exists, and isIncoherentAggregate (spec/data/aggregation-policy.ts) returns true for exactly one pair — percent with sum/count_distinct.
Why #16098 did not fix it
Two reasons, both structural rather than a scope preference:
- Carrying
returnType is a change to the host callback contract and its call site, not a row in measureResultType's table. The call site is in analytics-service.ts, which was fenced to a different open PR for that card.
returnType is OPTIONAL — "absent when the type can't be proven (an ambiguous/dyn expression)" — so even with the plumbing the rule answers for some formula fields and not others. That tiering is a design question worth stating rather than implying: is an unproven formula's measure column left as number, or is the absence itself the answer?
So #16098 records formula as its one not-on-this-input verdict, pinned by the enum walk, and files this.
What a fix would need
- A decision on the callback's shape: widen
sourceFieldMeta's return with returnType, or hand measureResultType the field definition instead of three extracted keys.
- The mapping from the four
returnType members to the DimensionType wire vocabulary (string / number / boolean / time / geo). Note text maps to string and date maps to time — the wire vocabulary is not FieldType, so this is a translation, not a pass-through.
- The absent-
returnType tier, which is the same "cannot answer, do not block" shape every other reader of sourceFieldMeta already uses.
Related: #16098 (the enumerated verdict per FieldType, where formula is left uncorrected and this is named as the reason) · #16101 (the temporal half, which introduced measureResultType) · #16099 (needs-user-decision: no layer refuses an incoherent aggregate / field-type pair).
Filed unassigned by the
os-devseat working #16098 (sessionhttps://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y). Not graded, nodomain:*— routing and priority are the triage seat's. Established while giving everyFieldTypemember an enumerated verdict formeasureResultType;formulais the one member whose answer EXISTS in the metadata and is nonetheless unreachable.What was measured
FieldSchemadeclares a formula field's result type, and its JSDoc names this exact consumer first (packages/spec/src/data/field.zod.ts):The dataset-measure side cannot read it. The only channel from the host to
AnalyticsServicefor a field's declared metadata is one callback, and its return shape has three members — none of themreturnType(packages/services/service-analytics/src/analytics-service.ts):So
measureResultType(aggregate, sourceFieldType)— which decidesAnalyticsResult.fields[].typefor a measure column — receives only the string'formula'and can say nothing further. Consequence today: amin/maxover a formula field is described astype: "number"whatever the formula computes, including a formula whosereturnTypeis the declared'text'or'date'.Live control that this is a reachable pair, not a hypothetical: nothing between the author and the driver refuses it.
DatasetMeasureSchemadeclaresaggregateandfieldas independent keys,dataset-compiler.aggregateToMetricTypechecks only vocabulary membership (UNSUPPORTED_AGGREGATESis empty), the threeensureCubesource-field gates ask only whether the column exists, andisIncoherentAggregate(spec/data/aggregation-policy.ts) returns true for exactly one pair —percentwithsum/count_distinct.Why #16098 did not fix it
Two reasons, both structural rather than a scope preference:
returnTypeis a change to the host callback contract and its call site, not a row inmeasureResultType's table. The call site is inanalytics-service.ts, which was fenced to a different open PR for that card.returnTypeis OPTIONAL — "absent when the type can't be proven (an ambiguous/dynexpression)" — so even with the plumbing the rule answers for some formula fields and not others. That tiering is a design question worth stating rather than implying: is an unproven formula's measure column left asnumber, or is the absence itself the answer?So #16098 records
formulaas its onenot-on-this-inputverdict, pinned by the enum walk, and files this.What a fix would need
sourceFieldMeta's return withreturnType, or handmeasureResultTypethe field definition instead of three extracted keys.returnTypemembers to theDimensionTypewire vocabulary (string/number/boolean/time/geo). Notetextmaps tostringanddatemaps totime— the wire vocabulary is notFieldType, so this is a translation, not a pass-through.returnTypetier, which is the same "cannot answer, do not block" shape every other reader ofsourceFieldMetaalready uses.Related: #16098 (the enumerated verdict per
FieldType, whereformulais left uncorrected and this is named as the reason) · #16101 (the temporal half, which introducedmeasureResultType) · #16099 (needs-user-decision: no layer refuses an incoherent aggregate / field-type pair).