Skip to content

A min/max over a formula field cannot be typed: sourceFieldMeta does not carry FieldSchema.returnType, the key whose own JSDoc names dataset measures as its consumer #16236

Description

@os-warren

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:

  1. 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.
  2. 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).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions