For the complete documentation index, see llms.txt. This page is also available as Markdown.

schema.yml

schema.yml is the structured definition of an entity — identity, keys, imports, features, metrics, and relationships.

The structured side of an entity. It defines what the entity is and what the agent can query on it — identity, keys, imports, features, metrics, and relationships.

What it is

schema.yml is what the agent reads to compose SQL: the columns and derivations it can select (features), the aggregations it can apply (metrics), and the paths it can traverse to other entities (relationships). Where ENTITY.md is prose the agent reads to understand the entity, schema.yml is structure the agent queries.

Everything rests on one rule: grain is preserved by construction. The entity always has exactly one row per base instance — one_to_many and many_to_many sources are handled through aggregation in metrics or row-selection in features, never through references that would multiply rows. Joins default to LEFT; a relationship step can set its own join_type when needed.

Where it lives

One per entity, alongside ENTITY.md:

.lynk/domains/<domain>/entities/<entity>/schema.yml

Format

The top-level fields:

identity: maindb.public.customers   # required — a physical table OR another entity
keys:                               # required when identity is a physical table; inherited otherwise
  - id

features: [...]
metrics: [...]
table_relationships: [...]
entity_relationships: [...]
imports: [...]                    # only when identity points at another entity
Field
Required
Type
Page

identity

path

keys

conditional

list

required when identity is a physical table; inherited when it's another entity — identity and imports

imports

object

only valid when identity is another entity — identity and imports

features

list

metrics

list

table_relationships

list

entity_relationships

list

One namespace. Feature, metric, and relationship names are unique within an entity, combined — a feature and a metric can't both be called total_points. The single namespace makes every reference unambiguous.

Expressions inside sql: and filter: follow the SQL expressions grammar.

Examples

A standalone entity with one feature and one metric.

An entity with a relationship feeding a cross-entity feature. Grove's customer pulls total MRR from its subscriptions.

Both sides of the step's join are declared features — id above, and customer_id on subscription's own schema.yml. Keys are not features; a step that references an undeclared column fails the build.

Validation

  • identity is present and valid; keys are authored when identity is a physical table — see identity and imports.

  • The field tables are exhaustive — an unknown key anywhere in schema.yml (a tags:, meta:, or time_grain: carried over from another tool) fails the build. There is no dimension/measure split and no time grain on a metric; time grouping is a query-time GROUP BY.

  • names are unique across features, metrics, and relationships combined.

  • Every feature and metric sql resolves to real columns and compiles at the Lynk build — the authoritative surface, not a raw-warehouse check (which is only a proxy and can be false-green). Unbacked columns or fabricated values fail the build.

  • The feature/metric dependency graph is acyclic — no feature or metric may transitively depend on itself. A definition on entity A can reference one on B, and a definition on B can reference back into A — that's fine, as long as the same definition never reappears in the chain (a.feature_a → b.feature_a → a.feature_a is the illegal case). Break a cycle by sourcing the looping value from the entity's own columns.

  • Each fact has one home — define it on the entity it belongs to and reference it elsewhere rather than restating it.

  • Each sub-definition validates per its own page: feature, metric, relationships.

Last updated