Object naming
Dot-name validation update (2026-10-02): The authored-name dot prohibition is disabled in SDK name validation and the shared AuthoredName schema. This supersedes the dot-prohibition statements below. Case-insensitive uniqueness and existing length checks remain unchanged. Quote-aware reference splitting, escaping and corresponding test-case updates are assigned to the SDK developer and deferred. Accepting a dotted name does not yet establish successful structural-reference resolution, preparation or target compilation; existing path parsing and encoding checks remain in place until that implementation is delivered.
This guide explains the authoring format and semantics. Target support varies; see Support and limitations.
This guide defines how Semantic Hub names objects, derives database identifiers, detects collisions, and resolves references. These rules apply to catalogs, connections, datasets, relationships, fields, hierarchies, metrics, models, and other named semantic objects.
1. One authored name
Every named object has one human-readable name. That value serves three purposes:
- It identifies the object uniquely among peers in the same scope.
- It is the name displayed by Semantic Hub authoring, BI, catalog, and MCP experiences.
- It is the value used by references to the object.
Semantic Hub does not define separate technical-ID and display-label properties. Maintaining both would duplicate identity and allow them to drift apart.
metrics:
- name: Total Order Revenue
definition: Revenue from accepted orders before returns
MetaKarta associates governance records with semantic objects independently. Changing governed ownership, stewardship, certification, or lifecycle metadata therefore does not require adding those properties to Semantic Hub YAML.
2. Authored-name requirements
An authored name contains between 1 and 63 characters and contains no dot (.), because dots delimit qualified semantic references. Uniqueness and reference lookup use the same case-insensitive comparison: compare names after lowercasing with Locale.ROOT, independently of the machine locale. Original spelling is retained for display and serialization. Spaces, punctuation, underscores, and Unicode characters are otherwise preserved; no trimming, character replacement, underscore collapsing, or Unicode normalization participates in semantic lookup.
Thus a>b, a<b, a b, a_b, a__b are distinct names. A B and a b are the same semantic name: either spelling resolves the same object, and declaring both in one peer collection is an error. Quote expression identifiers containing punctuation or spaces, for example "a>b"; structural references use the original authored spelling.
Quote characters are permitted inside an authored name and remain literal characters in its identity. YAML string delimiters are not part of the stored name: name: 'He said "yes"' stores He said "yes". In expression text, surround that identifier with double quotes and double each embedded double quote: "He said ""yes""". A single quote in a name is ordinary identifier content, for example "O'Brien"; YAML single-quoted strings double embedded single quotes (name: 'O''Brien'). Structural reference strings contain the original name or path, without expression-identifier delimiters; escape only for their enclosing YAML/JSON string syntax. Quoting never permits a dot inside an authored semantic name. Physical source-column names are separate external identifiers and may contain dots, addressed as quoted identifiers in expression text.
Within a catalog, datasets, models, and connections each have a separate namespace with unique names. Within each dataset, four separate namespaces require unique names:
- Columns: fields and metrics share this namespace.
- Joins: lookup uses the join path name; an unnamed join uses its target dataset name.
- Levels.
- Hierarchies.
The same name may occur in different namespaces or different datasets. A field and metric in the same dataset cannot share a name, even if their casing differs. Semantic uniqueness and lookup use the SDK's ShlNamedObject.nameKey comparison.
The row-expression namespace and its intentional same-name column override are described in section 7. Structural FieldReference values resolve authored fields only. Target namespace restrictions do not redefine semantic equality.
Unique keys have one deliberate authoring shorthand. When a dataset declares exactly one unique key, that key may omit name; Semantic Hub assigns the effective name default. A dataset with multiple unique keys must name every key explicitly; grain_key is optional. Without an explicit selection, a key named default selects grain; if no such key exists, grain remains unspecified until an operation requires it. Unique-key names are unique ignoring case within their own dataset namespace.
3. Target identifiers
Semantic references always use authored names. A target generator may quote those names directly or derive technical identifiers when its target requires them. Generated identifiers are an output mapping from bound objects, never keys for semantic lookup, dependency graphs, or cycle detection. Existing physical source-column names are preserved and quoted according to the source dialect; they are not renamed by this allocation.
The legacy SDK normalizeName / getNormalizedName helpers produce only a lossy target-name candidate: lowercase using a locale-independent rule, replace non-ASCII identifier characters with underscores, collapse underscores, and remove boundary underscores. They are retained for compatibility and must not be used for semantic uniqueness or reference resolution. A candidate is not guaranteed to be valid or unique for any target.
4. Naming examples
| Authored names | Semantic result | Possible target handling |
|---|---|---|
a>b, a<b |
Two distinct objects | Preserve quoted names, or allocate a_b_1, a_b_2 |
a b, a_b |
Two distinct objects | Keep separate object-to-identifier mappings |
Total-Revenue, Total Revenue |
Two distinct objects | Disambiguate if both yield total_revenue |
Revenue, revenue |
Same semantic name; duplicate peers are rejected | Both reference spellings resolve the one authored object |
commerce.v2 |
Invalid authored name | Dot is reserved for semantic paths |
Generated names shown here are illustrative, not a portable naming algorithm.
5. Reserved words and target limits
Target reserved words, identifier lengths, permitted characters, and case rules are checked at target generation. Where supported, quoting preserves the authored name. Otherwise the generator must allocate a valid technical identifier or return a target-specific diagnostic identifying the original object, target, and conflicting or invalid identifier. A target restriction does not make two semantic objects identical or require changing the business name globally.
6. Collision handling
Duplicate authored names under the case-insensitive comparison remain errors in their applicable semantic namespace. Names that merely produce the same sanitized spelling are valid distinct semantic names.
Target generators must retain an explicit mapping from object identity to emitted identifier. Allocation must be deterministic within the target scope, reserve existing and generated identifiers, and account for target case rules and length limits including any suffix. Truncation or suffixing must not introduce another collision. Flattened target namespaces need their own checks. A generator that cannot represent a collision safely must return a target-specific diagnostic; it must never silently merge objects, drop one, or redirect a reference.
Current target strategies vary: SQL query rendering quotes authored aliases, Tableau allocates internal identifiers, and some model targets reject unsupported target collisions. This contract does not claim that every target already supports automatic disambiguation.
There is no implicit fallback from an unresolved authored name to a sanitized spelling. Models relying on the former lossy lookup must preserve the authored punctuation, spacing, and underscores in references; differences in letter case remain accepted. Any future compatibility fallback must be explicit and reject ambiguous candidates.
7. Dataset expression and reference namespaces
7.1 Source columns in expression text
Every column returned by a dataset's table, view, file, worksheet, or query enters the dataset's inherited row-expression namespace as a source-column binding. Query-backed datasets use only columns returned by every query variant. Source-column names are external identifiers and use their own quoting and platform rules. Verified physical-column metadata retains its exact-name lookup; the semantic comparison does not rewrite physical identifiers. Source columns are not authored Semantic Hub objects.
Expression binding resolves source columns and authored fields through one effective row-expression namespace. This provides concise column expressions without reserving a name such as source:
fields:
- name: net_amount
expression: amount - discount
The bound expression retains whether each terminal name resolved to a physical column or an authored field. SDK trees and lineage must store stable column or field identity rather than only the terminal spelling.
Outside expression text, the two reference kinds are deliberately separate:
ColumnReferenceproperties such asunique_keys[].columns,joins[].from_columns, andjoins[].to.columnsbind directly to verified source/query output columns and never to authored fields.FieldReferenceproperties bind only to objects declared in a dataset'sfieldscollection, optionally through authored join-name path components. They never fall back to a source column.
Although both forms are serialized as YAML strings, the SDK resolves a FieldReference to stable field-object identity and retains a ColumnReference as a physical-column binding. To use a source column in a structural field property, first declare an authored field whose expression references that column.
7.2 Authored-field overrides
An authored field may intentionally have the same name as an inherited source column. It then replaces that column binding in the dataset's effective row-expression namespace and becomes the object targeted by structural FieldReference values:
fields:
- name: status
expression: COALESCE(status, 'UNKNOWN')
- name: is_open
expression: status = 'OPEN'
Binding is deterministic and independent of YAML order:
- Build the inherited row-expression namespace from source/query output columns.
- Build the authored-field namespace and detect duplicate authored names.
- While binding the overriding field's own expression, a reference matching its own name under the same case-insensitive semantic comparison skips the field being defined and resolves the same-name inherited column.
- In every other expression, the name resolves to the authored field.
If no same-name inherited column exists, a self-reference is a dependency cycle rather than a source-column reference. All other field dependency cycles are invalid. Validators and explain/lineage APIs should report every intentional column override and its affected dependents.
This is namespace construction, not a late "field if present, otherwise guess a column" fallback. If source metadata is unavailable or a name exists in neither layer, binding fails rather than inventing a column.
7.3 Join names and collisions
Join names are ordinary authored names. source has no reserved namespace meaning and may be used as a join name within the separate join namespace:
joins:
- name: source
to:
dataset: order_sources
Within one dataset, join names must be unique under the same case-insensitive semantic comparison. Fields and joins occupy separate authored namespaces, so both may be named customer: the reference kind and path position distinguish a terminal field from a join in customer.region. A reference with multiple valid interpretations must be rejected as ambiguous. A source-column/field same-name pair follows the override rule above.
Fields and metrics share the authored column namespace for uniqueness, while metric expressions and row expressions have distinct binding contexts. The expression binding context decides whether an unqualified terminal name may resolve a row value or a metric; it never tries both kinds and chooses one by precedence.
8. Semantic references and paths
References always use authored names, never derived SQL identifiers.
metrics:
- name: Average Order Value
metric_type: RATIO
numerator:
metric_ref: Total Order Revenue
denominator:
metric_ref: Order Count
Public Query references use an unqualified local name or dataset.member. The qualifier is an authored dataset name, never a join alias, and must resolve to an unambiguous reachable occurrence in the applicable root scope. Public query filters also require authored fields or permitted named metrics; they cannot address physical columns directly. Model authors expose contextual fields or metrics to encapsulate role-specific paths.
The following path rules apply to model-authored references and expressions, not public Query clauses. Qualified model references use dots as path separators:
| Form | Meaning |
|---|---|
Total Order Revenue |
Object in the current dataset or scope. |
Orders.Total Order Revenue |
Metric qualified by dataset. |
Customer.Region |
Field reached through the Customer relationship. |
Customer.Nation.Region |
Field reached through a multi-hop relationship path. |
Because a dot separates path components, dots are prohibited inside each authored name.
For a structural FieldReference path, every component except the last must resolve to an authored join in the dataset reached by the preceding components. The last component must resolve to an authored field object in that dataset. It never resolves to a physical column. In a model-definition binding context, the path may optionally begin with the name of a declared root dataset using the same case-insensitive comparison to select its anchor; that first component is a scope qualifier rather than a join. Other dataset names are not silently substituted for missing join names, and the resolver never selects an implicit or shortest relationship path.
An identifier path inside RowExpression, AggregationExpression, or FilterExpression uses the same explicit join traversal, but its terminal component resolves in the reached dataset's row-expression namespace and may therefore bind either an authored field or a verified source column. This is the only field-or-column form.
For an aggregation-level metric reference, an unqualified name resolves a metric in the current dataset. A qualified dataset.metric reference names the dataset explicitly; qualification selects the metric but does not assert compatible grain. Because the same two-part spelling can also denote relationship.field, a reference that validly resolves both ways is ambiguous and must be rejected rather than resolved by precedence. Properties whose schemas explicitly require join paths resolve every component as a join.
Semantic path components are case-insensitive, and the complete relationship path is part of reference identity. billing_customer.region and shipping_customer.region remain different references even when both joins reach the same dataset and terminal field. Inside expression text, a dot in a quoted physical column identifier is part of that identifier rather than a path separator. Structural FieldReference components are authored names and cannot contain dots.
Name resolution and semantic path legality are separate validation steps. A path can resolve successfully but still be illegal in a RowExpression when it crosses ONE_TO_MANY or another multivalued edge. RowExpression and FilterExpression permit only paths that yield at most one terminal value per anchor row. AggregationExpression resolves metrics at its outer phase; row paths are legal only inside explicit aggregate calls and are validated in the aggregate call's row binding context before output-grain alignment.
Semantic references use the same case-insensitive comparison as uniqueness checks, including quoted semantic identifiers and each qualified path component. Quoting preserves punctuation; it does not switch semantic lookup to case-sensitive mode. Visual editors and refactoring tools must update all resolved references when an object is renamed.
9. Validation responsibilities
JSON Schema validates authored-name length and prohibits dots. All properties that contain one authored name or an unqualified reference reuse Common.json#/$defs/AuthoredName. Qualified references, physical column references, source-platform identifiers, annotation names, and external-system names use their own schemas because they do not share the authored-name lexical contract. Qualified metric references, join paths, and structural field paths enforce the same 1-to-63-character limit on every authored component. External source-column quoting is available only in ColumnReference values and expression text.
The Semantic Hub catalog validator must additionally:
- Enforce authored-name uniqueness within every applicable scope.
- Build each dataset's source-column expression bindings and authored-field namespace from verified source/query output metadata and YAML fields.
- Resolve every structural
FieldReferenceterminal to an authored field object without column fallback. - Apply the authored-field override rule deterministically and reject unresolved or cyclic self-references.
- Enforce uniqueness separately in the column, join, level, and hierarchy namespaces; reject references with multiple valid interpretations.
- Validate and allocate target identifiers separately from semantic binding.
- Quote or disambiguate target reserved words where supported; otherwise return a target-specific diagnostic.
- Detect unresolved target identifier collisions before emitting artifacts.
- Resolve every path component by kind, retain the complete relationship path, and reject missing, ambiguous, or implicitly invented paths.
- Reject scalar row-expression paths that can return more than one value per anchor row.
- Verify that deployment namespace flattening does not introduce collisions.
Dependency graphs and cycle detection use resolved object identity, including whether a reference is an authored field or physical column. For example, field("a b") -> field("a b 1") -> column("a_b") is acyclic. Only an actual return to an active field object is a field cycle.
Naming diagnostics should include the original name, object type, containing scope, derived identifier, violated rule or target, and a concrete remediation.
Validation never rejects names merely because their normalized target-name candidates collide. Scoped validation reports actual case-insensitive authored duplicates using NAME_COLLISION; legacy catalog validation uses ERR_ADD_DUP_NAME. Named collections reject duplicate insertion with an IllegalArgumentException; those rejected objects never reach validation. Joins also retain the existing ERR_MANY_JOINS diagnostic.