Skip to content

Fix metric declared column check - #2529

Draft
shangyian wants to merge 3 commits into
mainfrom
fix-metric-declared-column-check
Draft

Fix metric declared column check#2529
shangyian wants to merge 3 commits into
mainfrom
fix-metric-declared-column-check

Conversation

@shangyian

Copy link
Copy Markdown
Collaborator

Summary

Test Plan

  • PR has an associated issue: #
  • make check passes
  • make test shows 100% unit test coverage

Deployment Plan

A metric's single output column is not named by its query. On deploy the
first projection's alias is overwritten with the amenable form of the node
name, so `shared.main.weekly_active_players` always persists a column named
`shared_DOT_main_DOT_weekly_active_players` no matter what the query aliased
it to, or whether it aliased it at all.

The declared-column check added in #2522 compares declared names against the
names the query itself produces, which for a metric is a name that never
survives deploy. Any metric whose query alias differs from its declared
column name, including the common case of no alias at all, failed validation
with INVALID_COLUMN. This broke real deployments: a semantic repo with 481
metrics declaring column blocks had 11 of them fail as soon as a query edit
sent them back through validation.

There is nothing for the check to assert here. `MetricSpec.columns` is an
internal field, excluded from serialization, sitting at ChangeTier.NONE, and
a metric's unit is authored at the metric level precisely so authors never
have to know the output column name. The declared metadata is reconciled onto
the single output column regardless of what it is called.

#2522's behavior is unchanged for transform, dimension, and source nodes,
where the declared name really does have to match a query output.
A metric's single output column is never named by its query. On deploy the
first projection's alias is overwritten with the amenable form of the node
name, so `shared.main.weekly_active_players` always persists a column called
`shared_DOT_main_DOT_weekly_active_players` whatever the query aliased, or
whether it aliased anything at all. A declared name therefore has nothing
stable to match, and the metadata attached to it was reconciled onto that one
column regardless.

The previous commit made metrics simply exempt from #2522's declared-column
check, which left authors free to write a `columns:` block that quietly did
nothing. Refuse it instead, and state the rule on the model rather than as a
special case in the checker: each spec class declares the fields the server
owns in `INTERNAL_FIELDS`, mapped to the remedy an author needs, and one
generic check turns any authored value into a `DJError`. The next
internal-only field is enforced by declaring it.

Distinguishing an authored value from a server-populated one needs no new
marker. `Node.to_spec` does pass `columns=` for metrics, but such specs come
from the branch copy, which sets `_skip_validation` and takes the fast path
above these checks; the export path drops metric columns entirely because the
field is `exclude=True`. So `columns` reaching the check means a human wrote
it. The check tests by value rather than by `model_fields_set` so that a
server-populated empty list still reads as unset.

This is a hard error with no grace period: every metric carrying a `columns:`
block now fails to deploy, not only those whose declared name mismatched.
`ErrorCode.INVALID_SPEC_FIELD` is new, so the generated GraphQL enum moves
with it. #2522's check is restored unchanged for transform, dimension, and
source, where a declared name really must match a query output.
An author who declared a field the server owns was told twice: once that the
field is not theirs to set, and again that the column they declared doesn't
match any column the query produces. The second message is a symptom of the
first and actively misleading here, since the rule is that a metric shouldn't
declare columns at all rather than that it named them wrongly. Skip the
declared-column check for any spec that already failed the internal-field
check. #2522's check is untouched; only whether it runs has changed.

Assert the whole error list by equality in these tests rather than probing it
for membership. Doing that to #2522's own transform test showed its fixture
names the spec `transform` while keying the graph on `test.transform`, so
`test.parent` never resolves and the case carries two more errors than it
looks like it does: nothing is inferred, and the declared columns are
unmatched partly for that reason rather than on their own merits. Recorded as
found, with a comment, rather than quietly repaired.

Also move `INTERNAL_FIELDS` down beside `FIELD_CHANGE_TIERS` and
`authored_internal_fields` below the tier classmethods, so neither splits a
block it was sitting in the middle of.
@netlify

netlify Bot commented Sep 9, 2026

Copy link
Copy Markdown

Deploy Preview for thriving-cassata-78ae72 canceled.

Name Link
🔨 Latest commit 0214434
🔍 Latest deploy log https://app.netlify.com/projects/thriving-cassata-78ae72/deploys/6aa0d833bb36190008ab0ff7

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant