Skip to content

Lint entity id references that no file in the folder holds - #56

Closed
escherize wants to merge 12 commits into
galdre/lint-parity-api-probesfrom
bryan/ver30-entity-refs
Closed

escherize wants to merge 12 commits into
galdre/lint-parity-api-probesfrom
bryan/ver30-entity-refs

Conversation

@escherize

Copy link
Copy Markdown
Contributor

Stacked on #51. Adds two lint rules, dangling-entity-ref and wrong-type-entity-ref, so the lint checks references between every entity type, not only transforms, tables and transform tests (Linear VER-30, VER-46).

Why, and where the line is

A remote-sync repo must be self-contained: everything a synced item needs must be in a synced collection (Metabase docs/data-modeling/semantic-layer/remote-sync.md:295-303,447). Metabase enforces that when content is saved, not on import: on import, a reference to an entity the repo doesn't hold falls back to the target instance, and fails with "<Model> '<id>' was not found" when it isn't there (serialization/v2/load.clj:176-184).

The rule follows the lint's existing principle (err on the side of what Metabase accepts) with a testable line: an error only when Metabase refuses on a fresh instance, which is what CI imports into. So:

  • A reference to an entity no file holds is an error.
  • Content every Metabase ships with is allowed: the 4 built-in transform tags (057_update_migrations.yaml:990-994) and instance-analytics content (104 entities, vendored from resources/instance_analytics at Metabase 714a9e49117 in vendor/instance-analytics.json, regenerated by scripts/update-instance-analytics.ts). Each allowed id counts only for its own model.
  • Out of scope on purpose: database, table and field references (Metabase stubs them instead of failing), and Metabase's import hooks and MBQL validity. Those track Metabase code that changes often, so mb ci leaves them to a real Metabase instance (VER-23, VER-51).

How it works

src/entity-refs.ts holds one plain finder per model (ENTITY_REF_FINDERS), each returning {path, entityId, allowedModels} and commented with the Metabase serdes/dependencies code it mirrors. The query and visualization-settings walker copies Metabase's mbql-deps (source-card, NanoID source-table, snippet-id, card-id, metric/segment/measure clauses, link and click-behavior targets, visualizer sources). Models: Card, Dashboard (dashcards, series, tabs, parameter mappings), Document (prose-mirror only), Collection, NativeQuerySnippet, Action, DataApp, Timeline, TableUserSettings, Metabot, Segment, Measure, Table, Transform, TransformJob, OsiAiContext. References already covered by dangling-transform-id and transform-collection stay with those rules.

Validation

  • bun test: 825 pass (was 799 on Compare transform API refusals with the lint's target rules #51). Unit tests per finder; mutations dangling-entity-ref and wrong-type-entity-ref with snapshots; tests confirmed to fail when the tab check or the legacy MBQL 4 branch is broken.
  • examples/v1: 0 findings. Metabase's own serialization baseline (165 files): 0 findings.
  • stats-remote-sync main (1,954 files, about 1,100 references checked): 0 findings. Deleting one Collection file in a copy gives 3 dangling-entity-ref, as expected.
  • Independent review: verdict ship; its fixes are the second commit.

Known gaps

  • Tabs inside instance-analytics dashboards are not allow-listed.
  • The vendored analytics list could join scripts/check-metabase-sources.ts; not done.

galdre and others added 12 commits October 30, 2026 16:57
Adds dangling-entity-ref and wrong-type-entity-ref, from a per-model table
of the entity ids each file references (src/entity-refs.ts), as Metabase's
deserialization-dependencies read them. The built-in transform tags and
common.py need no file.
Walk visualization settings, card parameter and dimension mappings, and
action settings as Metabase does; hold a [dashboard, tab] reference to that
dashboard's tabs; read only ProseMirror documents; allow-list instance
analytics content from a vendored list, and drop common.py.
@galdre
galdre force-pushed the galdre/lint-parity-api-probes branch 6 times, most recently from 7dbe8ca to 625f394 Compare November 4, 2026 21:32
@escherize

Copy link
Copy Markdown
Contributor Author

Bryan's agent: closing, this work was cancelled. mb ci stays in the CLI as is; the --experimental gate continues in metabase/metabase-cli#117.

@escherize escherize closed this Nov 5, 2026
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.

2 participants