Skip to content

xDBML Changelog ​

This file records substantive changes between xDBML specification versions. Patch-level clarifications and typo fixes within a published version are tracked in commit history rather than here.

The format is loosely based on Keep a Changelog, adapted for a specification rather than a software project.

v0.7.5 -- 2026 ​

Status: Draft -- current Released: 2026-10-31

A point release of the v0.7 draft on the flags of a relationship. §11.9 described foreign_master and inactive as flags without a value and undirected as a setting that takes true or false, and the tools read each of the three in its own way, on a Ref and on an Edge. The three now follow one rule: the name alone or : true sets a flag, and : false leaves it unset. Documents keep declaring xdbml: 0.7.

Changed ​

Spec ​

  • Relationship settings (§11.9): foreign_master, inactive and undirected are flags. A flag is set by its name alone or by : true, and left unset by : false, as by its absence; any other value is an error. undirected on an Edge reads the same way, as §11.16.2 states. §11.10 says that the foreign_master flag needs no value, rather than that it takes none.
  • Grammar (grammar/xDBML.g4): flagSetting takes the three flags, relationshipFlag, alone or with : true or : false, and edgeSetting takes undirected alone.

Tooling ​

  • Relationship flags (§11.9, §11.16.2, §13.3): the tools read undirected, inactive and foreign_master the same way on every element that takes them. The name alone sets a flag, as do : true and the quoted : 'true'; : false and : 'false' leave it unset, as absence does; any other value is an error. Before:

    • a Ref rejected undirected written alone (invalid-undirected), while an Edge accepted it, and an Edge accepted any value at all;
    • the diagram drew no arrows for undirected: true on a relationship between entities, and drew them for undirected: 'true', because the renderer read the setting as a string only;
    • inactive: false drew the relationship as inactive, and foreign_master: false made it a foreign master, because the tools tested only whether the setting was present. In a document declaring a version before 0.4, foreign_master: false also drew the version error of a v0.4 construct, and on a field without an inline ref:, the foreign-master-without-ref error.

    @xdbml/parse exports isFlagSet(), isInactive() and RELATIONSHIP_FLAGS, and isUndirected() takes an Edge as well as a Ref. The resolver reports invalid-undirected on an Edge as on a Ref, and invalid-inactive and invalid-foreign-master for a value other than true or false. The renderer and the relationship inspector of the playground read the flags through these functions instead of their own copies. @xdbml/parse accepts documents declaring xdbml: 0.7.5.

  • Grammar corpus: four v0.7.5 cases.

Not changed (compatibility) ​

  • The examples of the spec keep undirected: true, and tools keep writing foreign_master and inactive alone.
  • Two forms that parsed without an error now draw one: undirected with a value other than true or false on an Edge, and inactive or foreign_master with such a value, which read as set. inactive: false and foreign_master: false, which set the flag, now leave it unset.

v0.7.4 -- 2026 ​

Status: Draft -- superseded by v0.7.5 Released: 2026-10-29

A point release of the v0.7 draft that reads a type of several words written without quotes. A type such as timestamp with time zone is one name in double quotes, as in DBML, and tools write it so. Written without quotes in a field or a Type declared on a line of its own, the words now read as that name, with a warning. Before, the parser read the words after the first as further fields: an error at best, and with an even number of extra words, such as Oracle's TIMESTAMP WITH LOCAL TIME ZONE, fields named after the words, with no error. Documents keep declaring xdbml: 0.7.

Changed ​

Spec ​

  • Field types (§8.1): a type of several words is one name in double quotes. Written without quotes in the declaration of a field or a Type that stands on a line of its own, the words up to the settings or the end of the line, parameters included, read as that name and draw a warning.
  • Separators (§3.9): the end of the line ends a type written in words without quotes, as it ends a row of records.
  • Compatibility with DBML (Appendix D, item 10): the reading holds in every document, including one without a version declaration; DBML rejects the words.
  • Grammar (grammar/xDBML.g4): a comment on scalarType states the reading, which depends on the end of the line and so stays out of the rules.

Tooling ​

  • @xdbml/parse: reads the words after the name of a field, a Type, a polymorphic alternative, a tuple element or an array element as one type name, TIMESTAMP(6) WITH TIME ZONE included, when the declaration stands on a line of its own, and marks the ScalarType with unquotedWords. The resolver reports unquoted-type-name, a warning, for each one in the document's own declarations. @xdbml/parse accepts documents declaring xdbml: 0.7.4.
  • Renderer and playground: show the type as written. The help page of the diagnostics panel describes the warning.
  • llms.txt and the MCP server: the paragraph on types states the double quotes, with a PostgreSQL and an Oracle example, beside the type names an assistant picks from; the rule was stated only in the section on quoting. The common mistake of an unquoted type of several words names the warning.
  • Grammar corpus: five v0.7.4 cases.

Not changed (compatibility) ​

  • A document that parsed before reads as before, apart from a line that holds only two fields, with no separator, a int b int, which DBML rejects: it now reads as one field a of type int b int, with the warning. Fields that share a line with a brace, { a int b int }, or with a comma or a semicolon between them, read as before (§3.9).
  • In the brackets of array, set, map and union, a type of several words that follows no name still takes the quotes: array [double precision] keeps naming an element double of type precision (§8.5).

v0.7.3 -- 2026 ​

Status: Draft -- superseded by v0.7.4 Released: 2026-10-16

A point release of the v0.7 draft that renames one kind of transformation. derivation, the kind for a value computed within the row, is now calculation. The word "derivation" already names the derivation of a physical model, in this specification (§12.7) and in Hackolade Studio. v0.7.2 had just been published, so the old word is not kept as an alias. Documents keep declaring xdbml: 0.7.

Changed ​

Spec ​

  • Transformations (§27.10.1): the kind derivation is renamed calculation, in the list of kinds, the examples, the checks (§27.10.5), the drawing (§27.10.6), the OpenLineage table, where it still maps to DIRECT/TRANSFORMATION (§27.10.7), the validation summary (§27.12) and Appendix A.
  • Grammar (grammar/xDBML.g4): the comments name calculation, and state that a dependency between two entities takes identity only.

Tooling ​

  • @xdbml/parse: reads calculation as a kind of transformation, and reports derivation with unknown-transformation. @xdbml/parse accepts documents declaring xdbml: 0.7.3.
  • Renderer and playground: the pill of the kind reads calculation. The help pages on data lineage and the diagnostics panel name it.
  • llms.txt and the MCP server: teach calculation.
  • Example 19 and the grammar corpus use calculation; the corpus gains a v0.7.3 case where derivation is an error.

Not changed (compatibility) ​

  • A document of v0.7.2 that writes transformation: derivation gets an unknown-transformation error. Nothing else changes.

v0.7.2 -- 2026 ​

Status: Draft -- superseded by v0.7.3 Released: 2026-10-15

A point release of the v0.7 draft on data lineage. A Dep may now say how a target is loaded, and so serve as a source-to-target mapping: each dependency may carry a transformation, from a list of eight kinds that follows OpenLineage's column lineage facet, a line may name several source fields, and a Dep may carry the statement that loads the target in source_query, in the language its source_database names. A metric is a field of a View that is not materialized, whose lineage a Dep may state with transformation: aggregation. An entity or a View states its grain. Every document that parsed before still parses. Documents keep declaring xdbml: 0.7.

Added ​

Spec ​

  • Source-to-target mapping (§27.10): the kinds identity, derivation, aggregation, join, lookup, filter, group_by and conditional (§27.10.1), with a table of where each appears in a SQL statement. A line without transformation carries no kind. identity between two entities states a copy of every row and every field, such as a replica; the other kinds name a field. A type conversion the database makes on write keeps identity, and a CAST written in the statement makes a derivation. §27.10.2 covers the statement and its language, §27.10.3 the grain of the target, §27.10.4 the target fields no source supplies, with a Data Vault record source, §27.10.5 the checks, §27.10.6 the drawing, and §27.10.7 the correspondence with OpenLineage.
  • Metrics (§27.11): a metric is a field of a View that is not materialized; the View's source_query holds the SQL, and a Dep with transformation: aggregation states which fields each metric aggregates. A table gives the correspondence with Apache Ossie.
  • Settings of a Dep (§27.3): source_query, written in the body of a block as a View writes its own; source_database, with dialect as an alias, on a Dep and on a View (§14.5); transformation on a dependency. Values are read in any casing and written in lowercase. Without a version declaration they stay custom properties, as DBML 10.3.1 reads them.
  • Several upstream ends (§27.2): in a document that declares a version, a.x, a.y -> b.z or b.z <- a.x, a.y states one dependency per source field, with settings written once for all of them. The downstream end stays single.
  • grain (§7.5, §14.5): what one row of an entity or a View represents, in Kimball's sense. In a Metadata block it reads as a custom property, with a warning (§24.7.3).
  • Examples of §27.10 and §27.11 open in the playground from a "View in playground" button.

Tooling ​

  • @xdbml/parse: reads several upstream ends (DepDependency.upstreams), and collectLineage() gives one pair per upstream end, the pairs of one line sharing its line and settings. Each pair carries its transformation, and each Lineage its sourceQuery and sourceDatabase; dialect reads as source_database, on a View too. New diagnostics: unknown-transformation, transformation-between-entities and transformation-target-mismatch (errors), transformation-mismatch and indirect-from-entity (warnings), and source-query-in-settings for a Dep. New exports: TRANSFORMATIONS, targetCoverage() and grainOf(). Accepts documents declaring xdbml: 0.7.2.
  • @xdbml/render: a dependency with an indirect kind (join, lookup, filter, group by, conditional) draws dashed, and the ends of one such line meet at a junction of their own. A pill names the kind at the junction, which then draws no dot, or on the curve; a dashed border marks an indirect kind; identity shows none, except on a copy between two entities. Groups that end at one row or one header spread their arrowheads. Columns stand further apart when a dependency carries a kind. A selected Dep keeps its dashes and draws its pills above its lines, in the selection color. RelationshipVisibility.indirectLineage hides the indirect dependencies alone. A document without kinds draws as before.
  • Playground: a Dep with kinds reads in the inspector as a mapping table, one row per target field with its sources and kind, then the lines that steer the rows, then its statement highlighted as SQL. The inspector of a target lists the Deps that load it and the fields no dependency feeds, and, for a View that is not materialized, its metrics. Display > Lineage gains "Indirect dependencies". The help pages on data lineage, the diagnostics panel, the inspector, the diagram and visual cues cover the mapping.
  • Highlighting: Monarch and the TextMate grammar color transformation, dialect and grain as setting keys, and Monarch colors the eight kinds as values.
  • VS Code extension 0.7.2: the highlighting above.
  • llms.txt and the MCP server: a "Source-to-target mapping" section and a "Metrics" section, rows for the new settings in "What goes where", four common mistakes (a query in a note or a setting of another name, a Mapping block, several downstream ends, a kind between two entities), and a checklist item.
  • Grammar: depSpec takes a depEndList on each side of the arrow. grammar/test-cases.md gains a v0.7.2 section of 21 cases, each checked against the parser.
  • Example 19, source-to-target mapping: a legacy system and a CRM loading a warehouse, with every kind of transformation, a statement on each Dep, metrics in a View, replicas and a Data Vault hub, and three diagram views.
  • Example 18: the query of stg_customers moves from its note into source_query.

Changed ​

Spec ​

  • Rules of a Dep (§27.4): in a document that declares a version, the same pair may be stated once for each kind of transformation, since one field may play several roles in a load. The spec also states why cycles and two Deps with the same name are accepted, both as in DBML 10.3.1: a cycle may span several systems, as with reverse ETL, and a job that loads two targets takes one Dep per target, each with the job's name.
  • Levels in a block (§27.2): in a document that declares a version, a block may mix dependencies between entities with dependencies naming fields. Without a version declaration, DBML's rule of one level still holds.
  • Source query (§14.3): the spec states that neither the reference parser nor the playground parses a source query, of a View or of a Dep. A syntax error in the query, or a difference between the query and the fields or the Deps, draws no message; the playground's inspector highlights the query as SQL without checking it.
  • Validation summary of chapter 27 moves from §27.10 to §27.12, after the two new sections.

Not changed (compatibility) ​

  • Every document that parsed before parses, and reads the same. Without a version declaration, several upstream ends and mixed levels stay errors, as in DBML, and the new settings stay custom properties.
  • In a document that declares a version, a block mixing levels, an error before, is now valid, and so is the same pair stated twice with two kinds.

v0.7.1 -- 2026 ​

Status: Draft -- superseded by v0.7.2 Released: 2026-10-14

A point release of the v0.7 draft on names and settings. Chapter 17 takes the keywords EntityPartial and EntityGroup, after the entity they work on, as Entity names what DBML calls a Table; TablePartial and TableGroup, the DBML keywords, stay as aliases, and a diagram view lists Entities and EntityGroups beside Tables and TableGroups. color sets the header color of an entity, a View and an EntityPartial in a document that declares a version, as it sets the color of every other element drawn as a shape, with headercolor as an alias. A quoted setting key with the x- prefix, "x-hackolade-id", marks a custom property as an x_ key does, and a diagram view takes custom properties of any name, as every other element does: before, "x-hackolade-id" passed on an EntityGroup and failed on a diagram view. Every document that parsed before still parses, with one exception listed below. Documents keep declaring xdbml: 0.7.

Changed ​

Spec ​

  • EntityPartial and EntityGroup (§17): chapter 17 becomes "EntityPartial and EntityGroup". EntityPartial and EntityGroup are the keywords; TablePartial and TableGroup, the DBML keywords, are aliases that declare the same element, read in every document. DBML 10.3.1 rejects both new words, so no DBML document reads differently. Tools that write xDBML write the new keywords in a document that declares a version (§17.3, which until now kept the DBML names without synonyms). The rest of the specification names the constructs by their new keywords.
  • Diagram view categories (§18.1): Entities and EntityGroups list what Tables and TableGroups list; the DBML names are aliases, and an alias written beside the name it stands for is one category written twice. The new names are xDBML extensions, so a document without a version declaration writes the DBML names (§18.5, §18.6). The examples of chapter 18 use Entities.
  • Custom properties on a diagram view (§18.1): the brackets take note, and every other key is a custom property, prefixed or not, as on an EntityGroup, an entity and a field; a Metadata DiagramView block does the same (§24.7.3). Until now a diagram view took the x_ prefix only, and unknown-diagram-view-setting reported any other key. The rows of that condition leave the validation summaries of §18.6 and §24.7.6.
  • Metadata kinds (§24.7.1): EntityGroup targets an EntityGroup, as TableGroup does, in a document declaring xdbml: 0.7 or later.
  • Module system (§28.3): the element types entitypartial and entitygroup import an EntityPartial and an EntityGroup, as tablepartial and tablegroup do.
  • Header color (§7.5, §14.5, §17.1): color sets the header color of an entity, a View and an EntityPartial in a document that declares a version, with headercolor, the DBML name, as an alias. The two in one settings list are one setting written twice: a warning, and the first one applies (§24.5). In a Metadata Table or Metadata View block, color sets the header color too, and a block that writes both sets it twice, an error (§24.7.3). Without a version declaration, color on a Table stays a custom property, as DBML 10.3.1 reads it, and only headercolor sets the header color (Appendix D, item 13). §7.5 gains an example.
  • Custom-property prefixes (§24.5): a quoted key with the x- prefix, "x-hackolade-id", the form of the specification extensions of OpenAPI and AsyncAPI, marks a deliberate custom property as x_ does. DBML rejects a quoted key, so the form belongs to xDBML.
  • Reserved keywords (Appendix A), AST representation (§29) and DBML compatibility (Appendix D, items 13 and 15) list the new keywords, categories, kind and prefix.
  • Grammar (grammar/xDBML.g4): tablePartialDefinition and tableGroupDefinition take EntityPartial and EntityGroup, diagramViewCategoryKeyword takes Entities and EntityGroups, and metadataKind takes EntityGroup; the comments state the x- prefix and the reading of color.

Tooling ​

  • @xdbml/parse: reads EntityPartial and EntityGroup as TablePartialDeclaration and TableGroupDeclaration nodes, which gain a keyword field holding the word as written; reads Entities and EntityGroups as the categories Tables and TableGroups, with the keyword as written in keyword, as for Schemas; reads the Metadata kind EntityGroup, and the import element types entitypartial and entitygroup. In a document that declares a version, a color setting on an entity, a View or an EntityPartial, or in a Metadata Table or Metadata View block, gets the name headercolor and keeps color in nameSource, as required reads as not null, so every consumer, the renderer among them, reads one key. A diagram view and a Metadata DiagramView block take any custom property; the diagnostic code unknown-diagram-view-setting is no longer reported and stays in the DiagnosticCode type. Diagnostics name the keyword as written (EntityGroup member 'x' does not resolve), the duplicate-setting messages name both words when color and headercolor meet, and the messages of diagram views point to Entities and EntityGroups in a document that declares a version. New export: isCustomPropertyName(), true for an x_ or an x- key. @xdbml/parse accepts documents declaring xdbml: 0.7.1.
  • Playground: the field inspector lists x- keys under Custom properties with x_ keys, and labels a field received from a partial with the partial's keyword. The help pages on diagram views, Metadata blocks, the module system, the diagnostics panel, entity cards, the inspector and parse failures use the new keywords, categories and color.
  • Highlighting: Monarch and the TextMate grammar color EntityPartial, EntityGroup, the categories Entities and EntityGroups, Metadata EntityGroup, and a quoted "x-..." key before : as a custom property.
  • VS Code extension 0.7.1: the highlighting above.
  • llms.txt and the MCP server: write EntityPartial, EntityGroup, Entities, EntityGroups and color on an entity; custom properties may start with x_, or with x- in double quotes.
  • Examples: 09, 10 and 11 declare their groups with EntityGroup, and 16 lists its entities under Entities.
  • Grammar corpus: a v0.7.1 section on the new keywords, the categories, color and headercolor, and x- keys; the diagram view case with an unprefixed key moves from INVALID to VALID.

Not changed (compatibility) ​

  • Every document that parsed before parses, except the case below, and the DBML keywords keep working everywhere. The AST node kinds and the category names Tables and TableGroups stay as they were.
  • One reading changes: in a document that declares a version, color on an entity, a View or an EntityPartial was a custom property and is now the header color. A document without a version declaration reads it as before, as DBML 10.3.1 does.
  • One condition follows from it: a Metadata Table or Metadata View block of a document that declares a version and writes both color and headercolor sets the header color twice, an error, where color was a custom property with a warning.
  • A diagram view with a custom property other than an x_ one, an error before, is now valid.

v0.7 -- 2026 ​

Status: Draft -- superseded by v0.7.1 Released: 2026-10-12

Moves the DBML baseline from 3.13.6 to 10.3.1: a parser reads every construct DBML added in between, in every document, including one without a version declaration. Records: a column list on records inside a table, an example flag and empty fields; an entity holds one records block, as in DBML. Relationships: the ? marks DBML added to its operators make an end optional, and the default inference of §11.8 now makes the parent end optional for a nullable foreign key, where its table had put the zero at the child end. Custom properties and Metadata blocks: xDBML reads DBML's custom properties in every document, and a Metadata block sets a note, a color and custom properties on an element declared elsewhere, in the same file or an imported one. Sticky notes: a named top-level Note is an annotation drawn on the diagram, its text rendered as Markdown, with DBML's color and custom properties; it may sit in a Container, which carries it on the canvas, and a top-level Note without a name is the note of the project. Data lineage: DBML's Dep records which entity, View or field is computed from which, in a short form, a block and an inline dep setting; xDBML reads it in every document, lets its ends name Views and nested fields, and gives it a chapter of its own, chapter 27. Module specifiers: as in DBML 10.3.1, auth reads as ./auth, the host application may map a specifier such as @shared/users to a file, and an import without an extension falls back to a .dbml file. Every v0.6 document remains valid: in a document declaring v0.6 or an earlier version, a second records block draws a warning rather than an error, and a dep setting without an arrow stays a custom property, with a warning. Documents using the new rule declare xdbml: 0.7.

Added ​

Spec ​

  • Column list inside an entity (§26.1): records (id, email) { ... } populates the listed columns, as the top-level form does. Each column names a field of the entity, an injected field included. records starts a block before {, ( or [ (§3.10), so a field may still be named records.
  • Empty fields (§26.4): no value between two commas, or before the first comma of a row, reads as null. A row that holds fewer values than its column list leaves the remaining columns null, so DBML's 1, keeps its meaning while a comma after the last value stays ignored (§3.9).
  • Example records (§26.5): an [example] flag after the column list, or after records in a block without one, marks rows that illustrate the data rather than load it. A generator that emits data leaves them out (§26.7).
  • One records block per entity (§26.6): a second block for an entity, inside its body or at the top level, is an error, as in DBML; in a document declaring xdbml: 0.6 or an earlier version it is a warning.
  • Validation summary (§26.8) for records.
  • Optional marks (§11.1): a ? written against a relationship operator, >?, ?<, ?-?, ?<>?, makes the end on its side optional, in the short, long and inline forms, as in DBML 9.0 and later. The mark touches the operator; a mark apart from it, or a second one on a side, is a syntax error.
  • Settings and marks (§11.7): a setting states its end and the other end keeps the inference; a ? and a setting that disagree on an end draw a warning, and the setting applies in a document declaring a version, the ? in one without. Tools that write xDBML state cardinality with source: and target: and write no marks.
  • Marks on entity-level relationships (§11.16.1): a ? states the minimum of its end, 0, and leaves the maximum unstated.
  • Metadata blocks (§24.7): Metadata <kind> <target> { key: value ... } at the top level sets settings on an element declared elsewhere. DBML's kinds Table, Column, TableGroup and Note are read in every document; Entity, Collection, Record, Field, Container and its synonyms, View and DiagramView need xdbml: 0.7 or later. A Column target names a field by its path under its entity or View, nested and injected fields included.
  • Metadata settings (§24.7.3): a block sets note, the color of an element drawn as a shape (headercolor on a Table or a View, color on a TableGroup, a sticky note or a Container), and custom properties; a diagram view takes note and x_ properties. A setting xDBML defines elsewhere (synonyms, business_term, granularity, tags, a field setting on a Column, a color on the wrong kind) reads as a custom property, as in DBML, with a warning, beside the element's own setting.
  • Precedence (§24.7.4): the element's own declaration, then the blocks of imported files in import order, then the document's own blocks in source order, whatever their position relative to the imports. A block of the document that changes a value an earlier source set draws a warning; DBML applies it silently.
  • Imports (§24.7.5): blocks travel with the elements they target, renamed with an alias or when an entity leaves its Container; there is no metadata element type.
  • Validation summary (§24.7.6) for Metadata blocks.
  • Sticky notes (§19.2): a named top-level Note is a sticky note, an annotation drawn on the diagram and attached to no element, as the Annotation symbol of Hackolade Studio. The name is required, as in DBML; DBML's qualified form Note core.legend places the note in the implicit Container core. The colon form after a name, which DBML rejects, reads the same.
  • Settings of a sticky note (§19.3): color (#rgb, #rrggbb, or none for the text alone) and custom properties, as DBML 8.3 and 9.1 added them, read in every document; a color of another form is an error. A Metadata Note block sets the same settings.
  • Sticky notes in a Container (§19.4): with xdbml: 0.7, a named Note in a Container body is attached to the Container, drawn inside its frame and carried with it on the canvas; the rest of the document names it with its Container, sales.order_states. The unnamed Note of a Container body stays the Container's note.
  • The note of the project (§19.5): a top-level Note without a name is the note of the project, as a Note in the Project block; the Project block's note applies when it has one, otherwise the first unnamed note, and every other draws a warning. DBML rejects an unnamed top-level Note; a parser before v0.7 accepted it and left it out of the model. It takes no settings, and an import brings none.
  • Drawing (§19.6): a renderer draws a sticky note as a box in its color, or its default, holding the text rendered as Markdown (headings, emphasis, inline code, links, lists, block quotes, code blocks, thematic breaks), sized to the text; the document records neither its position nor its size.
  • Validation summary (§19.7) for sticky notes.
  • Data lineage (§27): a Dep records that an entity, a View or a field is computed from another by a transformation outside the model, such as an ETL job, a dbt model or the query of a view. It states no cardinality and constrains no value, and no generator emits anything for it. DBML added it in 10.0; xDBML reads it in every document, including one without a version declaration.
  • Endpoints and direction (§27.1): the arrow points from upstream to downstream, a -> b or b <- a, at four levels: entity to entity, field to field, entity to field (a count, an existence flag, a row hash) and field to entity (an array unnested into a table). An end names an entity, a View, or a field of either, a nested field with its path (§11.5, §11.6), resolved as a relationship endpoint is (§11.16.3), and declared in the document or brought by an import. An Edge, its fields and a composite endpoint are errors.
  • Declaration forms (§27.2): the short form, Dep name: a -> b [settings], with an optional name; the block form, which gathers dependencies into one downstream entity or View, at one level, with settings in its brackets and in its body as key: value lines or a Note, the body taking precedence, and settings after a dependency for that dependency alone; and the inline form, [dep: <- source] or [dep: -> target], on an entity, a View or a field. A block with no dependency draws a warning. A TablePartial carries no dep.
  • Settings (§27.3): note, rendered as Markdown, so a query fits in a fenced code block; color, #rgb or #rrggbb; and custom properties.
  • Rules (§27.4): a Dep stands at the top level; a field does not depend on itself, while an entity may; a dependency is stated once in the document, inline included; cycles are accepted; names are optional and may repeat.
  • Relationship, foreign master, Dep or View (§27.5), and lineage and the query of a View (§27.6): the Deps into a View and its source_query state its lineage separately, and neither overrides the other; a tool that reads the query's dialect may report a difference as a warning.
  • Drawing (§27.7): a dependency draws as a curve with an arrowhead at its downstream end, attached at the side of a field's row or of a box; the dependencies of one Dep that share a downstream end meet at a junction, which carries the Dep's name when the names of relationships are shown; a renderer offers a toggle that hides dependencies, and an automatic arrangement places each element to the right of the elements it is computed from.
  • Modules and Metadata blocks (§27.8), DBML compatibility (§27.9) and validation summary (§27.10, §27.12 from v0.7.2) for data lineage. xDBML differs from DBML 10.3.1 on three points: a block with no dependency draws a warning, a setting value of any form is a custom property, and brackets after a dependency in a block concern that dependency alone, where DBML applies them to the whole Dep.
  • Module specifiers (§28.13): a specifier other than an https:// URL resolves as in DBML 10.3.1, in every document. The host application may map it to a file first, as DBML's resolveFileSpecifier does; otherwise it is read relative to the importing file, with or without ./, so auth, sub/auth and @shared-users are valid, and an absolute path is an error unless the host resolves it. Without an extension, <path>.xdbml is read, or <path>.dbml when there is no such file.

Changed ​

Spec ​

  • §26.2: the top-level form no longer suggests one declaration per disjoint set of records.
  • §26.4: booleans and null are case-insensitive, and the readings of DBML apply by column type: a quoted number for a numeric column, 'Y'/'N', 'T'/'F', 1/0 for a boolean column, '' as null for a column whose type is not a string type.
  • §3.9, §3.10, §4, §32, Appendices A and D: aligned with §26. Conformance gains item 21.
  • §11.8: the default inference makes the parent end optional for a nullable foreign key, > giving '1..*' and '0..1', < giving '0..1' and '1..*'; through v0.6 the table put the zero at the child end, which contradicted §11.7. A foreign key is nullable when one of its attributes lacks not null and belongs to no primary key. <> keeps '0..*' at both ends.
  • §11.2, §32, Appendices A and D for the marks: conformance item 9 cites the marks and the precedence.
  • Grammar (grammar/xDBML.g4): recordsBlock and topLevelRecords are defined in the grammar rather than inherited from DBML, with the column list, the settings brackets and empty fields.
  • Grammar (grammar/xDBML.g4): an OPTIONAL_OPERATOR token, an operator with its ? marks, lexed as one so the mark touches it.
  • §24.5: DBML custom properties are read in every document, where a key without x_ is an ordinary DBML custom property; a setting written twice in one settings list draws a warning, the first applying, except check and ref, which repeat by design. DBML rejects a repeated setting.
  • §6.4, §14.5: color on a Container and headercolor on a View, which renderers already drew, are listed.
  • §24, §28.3, §29, §32, Appendices A and D for Metadata blocks: conformance gains item 22.
  • Grammar (grammar/xDBML.g4): metadataDefinition, top-level only.
  • §19: rewritten, from a list of the elements that carry a note and one example of a standalone note into seven sections (§19.1 to §19.7).
  • §5, §6.1, §18.2 to §18.4, §24.7.2, §28.1 to §28.3, §29, §32, Appendix D for sticky notes: a listed Container brings its sticky notes into a diagram view, narrowed by Notes as Tables narrows its entities; a Metadata block and an import name a note in a Container with its Container; the unnamed note of a file's project stays there on import. Conformance gains item 23.
  • Grammar (grammar/xDBML.g4): stickyNoteDefinition, at the top level and in a Container body.
  • Chapters 27 to 31 become 28 to 32: the new chapter 27, Data lineage, follows Records. Every reference to the Module system, the AST, the generators, the relationship to other standards and conformance moves with them, in the spec, the grammar, the test corpus, the examples, code comments and diagnostic messages.
  • What's new, §1.1, §11.13, §14.5, §18.1, §18.4, §24.7.1, §28.2, §28.3, §29, §31, §32, Appendices A and D for data lineage: declared lineage is in scope and runtime lineage, which OpenLineage records, is not; a dependency draws as a curve, unlike a relationship; refresh_on implies no Dep; a diagram view shows each dependency whose two ends are members; no Metadata kind targets a Dep; use * brings the Deps of a file, and there is no dep element type; the AST gains a Dep node. Conformance gains item 24. The FAQ gains a question on data lineage.
  • Grammar (grammar/xDBML.g4): depDefinition, depSpec, depBodyItem and depSetting, and the arrows ARROW_RIGHT (->) and ARROW_LEFT (<-), each lexed as one token.
  • §28.13, §28.14, §28.14.1, §28.17 for module specifiers: a bare host such as github.com/owner/repo/... names a folder, as in DBML, where through v0.6 it was rejected; inside a remote module a specifier resolves against the module's URL without the host's mapping, and a Windows drive or network-share path is an error. Conformance item 15 of §28.17 rejects an absolute path the host does not resolve, rather than a path without ./ or ../.
  • Baseline (What's new, §1.2, §4.1, §5.3, §30.1, Appendix D): xDBML is a strict superset of DBML 10.3.1, and a document without a version declaration reads as DBML 10.3.1. Appendix D is rewritten against 10.3.1: item 11 lists the constructs DBML added between 3.13.6 and 10.3.1, which a parser reads in every document, and the two that keep their v0.6 reading in a document declaring v0.6 or an earlier version; item 12 lists the points where an xDBML parser reads DBML 10.3.1 differently, none of which makes a valid DBML document invalid; item 13 names the extension constructs among them. The sections that dated a construct from the 3.13.6 baseline give the DBML version that added it.

Tooling ​

  • @xdbml/parse accepts documents declaring xdbml: 0.7. A records block inside an entity carries columns when it has a column list; both records forms carry settings and example; an empty field is a NullValue with empty: true. The brackets before a column list draw a syntax error naming the order. New diagnostic: duplicate-records-block. A column list inside an entity is checked like the top-level one (unresolved-records-column).
  • Editors: records takes the block color before ( and [ as well as {, and example the color of a setting flag.
  • @xdbml/parse, relationships: RefSpec and RefValue carry sourceOptional and targetOptional beside the operator, which keeps its four values. refCardinality() and refCardinalityIn() return the cardinality of each end with its origin (setting, operator, optional-mark, nullable-foreign-key, unstated), and refChildNullable() whether a child end may hold null. New diagnostic: cardinality-conflict.
  • Diagram: each end draws the cardinality @xdbml/parse states for it, so a nullable foreign key shows the optional ring at the parent end, <> shows it at both ends, and a ? on a relationship between entities shows the ring alone. A label shows only a range that a setting states and that applies. The foreign master lines of examples 12 and 13 gain rings, their copied fields being nullable.
  • Playground inspector: a relationship shows its operator with its marks, and a Cardinality section gives each end's range and what states it, with a note when a mark and a setting disagree.
  • Editors: an operator and its ? marks color as one operator.
  • @xdbml/parse, Metadata: a MetadataDeclaration node (targetKind, targetKindSource, target, targetSource, settings, bracketSettings, file, imported). flatten() lists Metadata blocks after every other statement, in their order of precedence, those an import brings marked imported. metadataIndex() returns the settings blocks apply to each element, withMetadata() merges them with an element's own, metadataNote() and metadataColor() read a note and a color, and resolveMetadataTarget() resolves a target. A selective import brings the blocks of its elements. New diagnostics: unknown-metadata-kind, unresolved-metadata-target, duplicate-metadata-setting, invalid-metadata-value, metadata-custom-property, metadata-brackets-ignored, metadata-override, duplicate-setting; diagnostics concern the document's own blocks.
  • Diagram: a header color, a TableGroup color and a Container color that a Metadata block sets apply over the element's own.
  • Playground inspector: the settings and the note of an entity, a View, a field, a Container and a diagram view include those Metadata blocks set, each such setting marked "Metadata".
  • Editors: Metadata and its kind color as declaration keywords, only together, so an entity or a field may still be named metadata.
  • Renderer goldens 12 and 13: restored without the C2PA manifest that the delivery of topic 2 had written into them, which made the renderer test suite fail.
  • @xdbml/parse, sticky notes: a NoteDeclaration carries settings, and its name is held in the form of names.ts, so a qualified name and a quoted name holding a dot read as elsewhere; a Container body holds NoteDeclaration beside its own NoteBlock. stickyNotes() lists the sticky notes with their ids (sales.order_states), resolveStickyNote() resolves a name, projectNote() returns the note of the project, and isStickyNoteColor() checks a color. Diagram views, Metadata blocks and selective imports resolve a note in a Container; importing everything leaves the unnamed note behind. New diagnostics: invalid-note-color, duplicate-project-note; a sticky note in a Container without xdbml: 0.7 draws construct-requires-version, and two notes of one name in one scope duplicate-declaration.
  • @xdbml/parse, Metadata fixes: a color a kind takes (headercolor on a Table, color on a Note) no longer reads as a custom property, so a block that changes it draws the override warning and replaces the element's own in withMetadata(); the warning on a setting written twice says that the first one applies, as §24.5 states.
  • Diagram: sticky notes are drawn as boxes with a folded corner, light yellow by default, holding their Markdown text, in the font size of a field name, a heading no larger than an entity's name, 220 px wide, widening to 280 and 340 px before a long text makes the box taller than wide, and at least half as tall as wide. A note in a Container stacks below its entities inside the frame, which widens to hold it; the other notes take a column of their own, and auto-arrange places them in rows below a Container's entities or in a block of their own. Notes keep positions per diagram view, like entities. DiagramModel gains notes, and @xdbml/render exports parseMarkdown(), parseInline(), layoutNoteText() and sizeNote().
  • Interactive mount: a sticky note is dragged by its whole box and selected by a press without a move ({ kind: 'note', id: 'note:<name>' }); dragging a Container carries its notes; the stickyNotes visibility option hides them; viewportCenter() returns the canvas point at the center of the view.
  • Playground: an "Add sticky note" button in the diagram toolbar writes a note into the source, in the selected Container when the document declares xdbml: 0.7, and into the Notes of the diagram view shown, then places it at the center of the view and selects it; Monaco's undo takes it back. The Display menu gains a Sticky notes option. A sticky note inspector shows the name, the Container, the color, custom properties and the rendered text; every note in the inspector renders its Markdown; the diagram inspector counts sticky notes and shows an unnamed note as the note of the project; a Container lists its notes.
  • Editors: Note colors as a keyword before the name of a sticky note followed by settings brackets, a qualified name, or the colon form.
  • @xdbml/parse, data lineage: a DepDeclaration node (name, form, settings, bodySettings, headerSettings, dependencies), each DepDependency with its upstream and downstream endpoints, the arrow as written and its own settings; an inline dep setting holds a DepValue (arrow, target). collectLineage() lists every Dep, declarations first, then the inline settings, with an id dep:<n> that the diagram and the inspector share; lineageById(), depSettings(), which applies the precedence of the brackets and the body, depColor(), isDepColor() and checkLineage() complete the API, with resolveLineage(), which resolves the two ends of each dependency to an entity or a View and the field in it. flatten() moves a Dep that a Container-scoped import brings to the top level. New diagnostics: unresolved-dep-endpoint, invalid-dep-endpoint, ambiguous-dep-endpoint, dep-mixed-downstream, dep-mixed-level, dep-self-loop, duplicate-dep, empty-dep, invalid-dep-color, dep-wrong-kind, dep-in-partial, misplaced-dep, invalid-dep-value; a Dep in a body, a relationship operator, a space inside an arrow and a dependency with more than two ends are syntax errors.
  • Diagram: a dependency draws as an amber curve (#d97706, #f59e0b in the dark theme), or in the color of its Dep or of the dependency itself, from the side of its upstream end to an arrowhead at the side of its downstream end, at a field's row or at the header of an entity or a View; the dependencies of one Dep that share a downstream end meet at a junction, and the Dep's name shows there with the relationship names. Auto-arrange places sources on the left and targets on the right: a block whose entities lineage mostly links is laid out in layers, sources in the first column and each entity in the column after those it is computed from, in columns of eight at most; elsewhere, a dependency counts as a link and its source stands left of its target where the grid allows; and the Containers that lineage links stand in columns, sources on the left, the others below. The canvas, and the fit of the view, hold a curve that loops out past its boxes, as between two boxes of one column. A diagram view draws the dependencies between its members. DiagramModel gains deps, the theme gains lineage, and @xdbml/render exports layoutDeps() and layoutDep().
  • Interactive mount: a click on a curve selects its Dep ({ kind: 'dep', id: 'dep:<n>' }): its curves, junctions and arrowheads turn to the selection color, the rows of the fields at its ends take the selection tint, and the boxes it names with no field are outlined. The serializer's highlightedFields option tints rows besides the selected field. The lineage visibility option hides dependencies.
  • Playground: a Dep inspector shows the name, the form, the color, its dependencies read back as sentences that name the elements, one per downstream element (stg.full_name is computed from raw.first_name and raw.last_name; the rows of fct_orders are computed from the rows of stg_orders), each with the settings written after it in a block, the transformation its note describes, rendered as Markdown, and the custom properties. The Display menu gains a Lineage heading with a Dependencies option; the diagram inspector counts Deps; the help pages on visual cues and on relationships describe the curves.
  • Editors: Dep colors as a declaration keyword at the start of a line before :, {, [ or a name, so a field may still be named dep; dep colors as a setting key in brackets; -> and <- color as the arrows of a dependency, amber in the playground.
  • Examples: 18-data-lineage, an analytics warehouse fed by a shop's database and the Stripe API, with the three forms, a junction, a Dep in its own color, and dependencies from an entity to a field.
  • llms.txt and the MCP server: a Data lineage section, a Dep row in the table of what goes where, two common mistakes (a relationship operator in a Dep, an inline dep on a field naming an entity), and Dep among the top-level declarations of the checklist.
  • @xdbml/parse, modules: ParseOptions.resolveSpecifier(specifier, importerPath) maps a specifier to the key handed to readFile, or returns undefined for the default resolution of §28.13. An import without an extension asks readFile for <path>.xdbml, then for <path>.dbml, as §28.13 has stated since v0.2; through v0.6.7 the parser asked only for the first and read ./lib.dbml as lib.dbml.xdbml, so a DBML multi-file project did not resolve. A Windows drive path reads as an absolute path rather than as a URL scheme c:. A source whose first folder looks like a host is read as a folder, and the error when it holds no such file gives the https:// form. moduleCandidates() returns the keys a source resolves to.
  • Grammar corpus: a §28.13 section, with the fixture legacy.dbml; the cases for an absolute path and for a URL, written for v0.2, now test §28.13 and the http: rejection of §28.14.
  • llms.txt and the MCP server: documents declare xdbml: 0.7, and the checklist places Metadata blocks at the top level. The MCP server's instructions give DBML 10.3.1 as the baseline and xdbml: 0.7 as the first line; its reference test reads a part without a version line as xdbml: 0.7.
  • Playground: the starter schema, example 02, declares xdbml: 0.7. New help pages on sticky notes, data lineage and Metadata blocks; the pages on the diagram, the inspector, relationships, the module system, the diagnostics panel and the visual cues cover the constructs of v0.7, the optional ends a ? or a nullable foreign key draw among them.
  • VS Code extension 0.7.0: highlights Metadata blocks, sticky notes, Dep, the arrows of a dependency, the ? marks of an operator, and the new forms of records.
  • Site: the home page, the README, the FAQ, the governance model and the 5-minute introduction give DBML 10.3.1 as the baseline and v0.7 as the current draft; the version table lists data lineage and module specifiers under v0.7.

v0.6.7 -- 2026 ​

Status: Draft -- superseded by v0.7 Released: 2026-10-10

A point release of the v0.6 draft on quoted names. §3.2 defines quoted identifiers, and Appendix D makes every DBML 3.13.6 document valid xDBML, but @xdbml/parse read a quoted identifier only as the name of a declaration and after a dot in a path. A key, an index or a relationship on quoted names did not parse, nor did a DBML 3.13.6 document that used them, as @dbml/core writes relationships: Ref:"users"."id" < "posts"."user_id". The tools now read a quoted name in every place a name stands, and keep a dot inside quotes in the name. Every document that parsed before still parses, and documents continue to declare xdbml: 0.6.

Changed ​

Spec ​

  • Identifiers (§3.2): the section lists the places where a quoted identifier stands, and states how a qualified name reads. Each segment is bare or quoted on its own, "sales"."Work Order"; a dot outside quotes separates two levels, and a dot inside quotes is part of the name, so "my.table" is one entity and my.table is the entity table of the Container my. A note gives the state of @xdbml/parse before v0.6.7.
  • Path syntax (§20.1, §20.2): a path starts with a name, bare or quoted, as in "Work Order Part"."Work Order ID", and a dot inside a quoted segment is part of the name.
  • Lexical grammar (Appendix B): a name is an identifier or a quoted identifier, in a qualified name, a composite reference and a field path.
  • Grammar (grammar/xDBML.g4): a nameSegment rule, an identifier or a quoted identifier, stands in every rule that takes a declared name, an entity reference, a path head, the fields of a composite relationship, an import path or alias, a ~name line, and the name of a Project or a Ref.

Tooling ​

  • Quoted names in every name position: @xdbml/parse reads a quoted identifier at the head of a path, so in the key lines of a constraints block, in indexes, and at both ends of a Ref, inline or long; in the fields of a composite relationship, "Order Line".("Order ID", "Line No"); in each segment of a dotted entity, Enum or type name, Table "sales"."Work Order"; as a TableGroup member; in a top-level records declaration and its columns; after ~; in an import path and its alias; in a tuple element or a polymorphic alternative; and as the name of a Ref, Ref "fk order customer": .... It used to reject these forms, at the head of a path with Expected path start identifier.
  • A dot inside quotes stays in the name: the parser read "my.table" and my.table as one name, so a quoted name holding a dot reached the entity table of the Container my, or collided with it. The AST now holds a qualified name with each segment that holds a dot or a double quote written in quotes, as in the source: sales.Work Order for "sales"."Work Order", "my.table" for "my.table". A name without such characters reads exactly as before. @xdbml/parse exports joinName(), splitName(), quoteNameSegment(), nameQualifier(), lastNameSegment() and isQualifiedName() for this form, and the resolver, the key and relationship checks, the module resolver, the diagram and the playground inspector use them, so a field named "user.id" and the nested field user.id stay two fields, in a key as in the diagram.
  • @xdbml/parse accepts documents declaring xdbml: 0.6.7.
  • Playground: diagnostics to the clipboard: the text of the diagnostics panel can be selected and copied with Ctrl+C or Cmd+C; a click that ends a selection no longer jumps to the source, which took the focus to the editor and dropped the selection. A Copy button in the header of the panel copies every diagnostic, one per line, with its severity, line, column, code and message. The playground help describes both.
  • llms.txt: one line in "Quoting" on quoting each segment of a qualified name.

Not changed (compatibility) ​

  • Backward-compatible. Every document that parsed before parses to the same AST, unless it declares or references a quoted name that holds a dot or a double quote: such a name now keeps its quotes in the AST, and reaches only the declaration of that name.
  • A plain DBML document with quoted names in a key, an index or a relationship, which @xdbml/parse rejected, now parses as it does in DBML 3.13.6. ~"name" is the one form @xdbml/parse reads where DBML 3.13.6 rejects it.

v0.6.6 -- 2026 ​

Status: Draft -- superseded by v0.6.7 Released: 2026-10-06

A point release of the v0.6 draft on TablePartials. §17.1 gave the conflict rule alone; it now states where the fields of a TablePartial go, which bodies take a ~name line, and what a TablePartial carries to them. The tools apply it: the parser kept a ~name line without applying it, so the diagram, the relationships and the checks did not see the injected fields. Every document that follows a form the spec described remains valid, and documents continue to declare xdbml: 0.6.

Changed ​

Spec ​

  • TablePartial (§17.1): the section now states where injected fields go and which bodies take a ~name line. A ~name line places the fields of the TablePartial in the entity at the position of the line, in the order the TablePartial declares them. A field the entity declares itself overrides an injected field of the same name and keeps its own position; among partials the last one injected applies, at the position of its ~name line. This is the behavior of upstream DBML. A ~name line may also sit among the fields of an object { } or a json { }, and in the body of a Type or of an entry of a definitions block, with the same rules; the grammar and the parser already read it there, and no earlier version of the spec described it. Only the fields are placed in a nested body: the keys and checks of the partial's constraints block apply at the top level of an Entity or an Edge. ~ names a TablePartial, never a Type. At the top level of an Entity or an Edge, a TablePartial brings its keys and checks, the entries of its indexes block, and its header color and its note where the entity declares none, the last partial injected applying among several. This is the behavior of upstream DBML as well. Its other settings stay with it. A TablePartial does not inject another TablePartial: a ~name line in the body of a TablePartial, or inside one of its fields, is an error (partial-injection-in-partial). Upstream DBML rejects that form as well. The parser used to accept it without a diagnostic.
  • Type and TablePartial (§15.6): the comparison table follows §17.1. A Type is named as the type of a field, which holds its shape as a nested value; a TablePartial is injected with a ~name line and its fields join those of the body.
  • Edges with named types and partials (§13.5): the example injected a Type with ~, which is an error (unresolved-partial). It now declares a TablePartial for the injected properties and a Type for a nested one.
  • Records (§26.1): the implicit column order of a records { } block in an entity that injects a TablePartial is the order of §17.1, each injected field at the position of its ~name line. The section used to list the entity's own fields first, then the injected ones.
  • Indexes (§9): one sentence on the indexes block of a TablePartial, whose entries add to the indexes of each entity that injects it (§17.1).

Tooling ​

  • TablePartial injection applied (§17.1): the parser kept a ~name line as a node of the body and nothing applied it, so the fields of a TablePartial were missing wherever a tool read the fields of an entity. @xdbml/parse exports effectiveFields(), which returns the fields of an Entity or an Edge with the injected fields in place, each with the TablePartial it comes from, and effectiveFieldList() for the fields of an object, a json body or a Type, next to tablePartials() and injectedPartials(). With them:

    • the diagram lists the injected fields in the entity, in an Edge box and in the rows of a nested object, a json body or a Type, with their PK, unique and relationship marks. The PK mark follows §10.4: the primary key of the entity applies over the one of a partial, and among partials the last one injected;
    • a Ref endpoint that names an injected field resolves, at the top level and along a nested path. It used to report unresolved-field, as did a column of a top-level records declaration and a key path in constraints;
    • an inline ref: on a field of a TablePartial draws one relationship per place that injects the partial, and the referenced-key rule (§11.17) checks it at the top level;
    • a ~name line in the body of a Type or of an entry of definitions that names no TablePartial reports unresolved-partial, as it did in an entity;
    • in the playground, a click on an injected row opens the field, the inspector names its TablePartial, and Edit in source goes to the line in the TablePartial. The field count of the entity includes the injected fields.
  • Indexes, header color and note of a TablePartial (§17.1): @xdbml/parse exports effectiveIndexes(), effectiveHeaderColor() and effectiveNote(). The diagram draws an entity in the header color of a partial it injects, over the color of a TableGroup; the inspector shows that color and the note of the partial; and a unique index of a partial is a key a relationship may reference (§11.17).

  • One diagnostic (§17.1): partial-injection-in-partial (error) on a ~name line in the body of a TablePartial or inside one of its fields, whatever version a document declares. The playground help lists it.

  • @xdbml/parse accepts documents declaring xdbml: 0.6.6.

  • llms.txt: a "Partials" section, with an example checked against the parser, and the TablePartial and Type rows of "What goes where".

  • Renderer: automatic arrangement (autoArrange, relational and star): two changes to the first layout of a diagram, the one the playground draws for a document without saved positions and the one renderToSVG draws by default. The free cell next to a placed entity is now searched beside it first, then above or below it, and across a corner last; the second entity of a related pair used to land across a corner, a row lower, and with rows as tall as the tallest entity of the diagram it could sit far below its neighbour, as claim_submission did in example 17. The blocks of the containers, and of unconnected groups of entities, are packed in rows of the width that lets a landscape view show the arrangement at the largest zoom, instead of a width derived from the total area. Over the seventeen examples, relationships whose two entities share a row or a column go from 27 of 63 to 59 of 63, and the zoom at which a 1100 by 700 pane fits the whole arrangement rises by 22% on average. A saved layout is unchanged; Arrange in the diagram toolbar applies the new arrangement to it. The goldens of sixteen examples change accordingly.

Not changed (compatibility) ​

  • Backward-compatible for every form the spec described. A document that injects TablePartials now resolves the references to their fields, which used to draw unresolved-field.
  • One form that @xdbml/parse read without a diagnostic now draws an error: a ~name line inside a TablePartial. No version of the spec described it, and upstream DBML rejects it.
  • A plain DBML document that uses TablePartials now reads as it does in DBML for fields, indexes, header color and note. One difference remains, stated in §10.4 since v0.6: when an entity and a TablePartial both declare a primary key, the entity's applies, where DBML combines them.

v0.6.5 -- 2026 ​

Status: Draft -- superseded by v0.6.6 Released: 2026-10-03

A backward-compatible point release of the v0.6 draft. An entity may declare internal definitions: named types visible only inside the entity, which a JSON Schema document holds under $defs (§15.8). The Type declarations of v0.1 stay the reusable types of the whole Project. Every v0.6 document remains valid, and documents continue to declare xdbml: 0.6.

Added ​

Spec ​

  • Internal definitions (§15.8): a definitions { } block in the body of an entity holds entries in the form of a Type declaration without the Type keyword, object-shaped or scalar. An entry is visible to the fields of its entity and to the other entries of its block, and nowhere else (§15.8.2). A type name in the entity resolves among the entries before any declaration outside the entity; an entry under the name of a Type or an Enum otherwise visible draws a warning; the fields an entity receives from a TablePartial resolve where the TablePartial is declared. An imported entity brings its block along, and a field imported on its own takes the shape of the internal definition it names (§15.8.3). The block is an error in an entity whose target is relational, Cassandra, ScyllaDB, Neo4j, Memgraph, Neptune or JanusGraph (§15.8.4). A generator writes the entries as $defs or definitions in JSON Schema, named records in Avro and nested messages in Protobuf, and inlines them for other targets, MongoDB included (§15.8.5). §15.8.6 lists the conditions a parser reports.

  • Conformance (§31): item 20, reading internal definitions.

  • Appendix A: definitions among the block keywords.

Tooling ​

  • Parser (@xdbml/parse): reads definitions { } in an entity body, only before {, as a DefinitionsBlock whose entries are TypeDeclaration nodes in the object or the scalar form; the entries form a list body. A definitions block in a View fails the parse with a message citing §15.8.1. Inside an entity, a type name resolves among its entries before any declaration outside it: in field types, in near-miss suggestions, in Ref paths and composite endpoints, and in the key paths of constraints. A name written in the body of a project Type, or in a field received from a TablePartial, resolves at project scope. resolveNames() reports definitions-outside-entity, duplicate-definitions-block, duplicate-definition, named-type-shadows-builtin (for an entry) and definitions-unsupported-target as errors, and definition-shadows-type as a warning, whatever version the document declares (§15.8.6). A field imported on its own takes the shape of the internal definitions it names, and the import fails with a message naming the definition when it reaches a recursive one (§15.8.3). New exports: entityDefinitions(), checkDefinitions(), inlineInternalDefinitions(), declarationTarget(), isDefinitionsExcludedTarget(), and the LocalTypes type. The target helpers move from constraints.ts to a new targets.ts; constraints.ts re-exports isRelationalTarget, so existing imports keep working. The editor colors definitions before {. Reads xdbml: 0.6.5.

  • Grammar: definitionsBlock, definitionEntry and DEFINITIONS, with definitions among the field names. grammar/test-cases.md gains thirteen v0.6.5 cases, each checked against the parser, two of them importing from the new fixture grammar/fixtures/crm.xdbml.

  • Renderer (@xdbml/render): a field typed by an internal definition expands in the diagram like a field typed by a Type. Each entity resolves names among its own internal definitions before the project's Types, and an expansion that enters a project Type resolves the names of its body among the project's Types only, as §15.8.2 states. The recursion guard tracks declarations rather than names, so an internal definition and a project Type of one name do not stop each other. The renderer imports entityDefinitions from @xdbml/parse, so its own build needs the 0.6.5 parser.

  • Playground: the inspector resolves a row inside an internal definition with the same scoping, and the entity inspector lists the internal definitions of the selected entity, each with the field count of the object form or the base type of the scalar form. The help pages cover the five diagnostic codes, internal definitions in the nested-fields page, and the new inspector section. The §15.8.1 example of the spec opens in the playground.

  • Example 17, internal definitions: an insurer's claims intake across three targets. The JSON Schema entities declare their shapes in definitions blocks, one of them recursive and two used as oneOf alternatives; a MongoDB collection declares an Address of another shape under the same name; an Oracle table reuses the Project-level Type MonetaryAmount, since relational targets take no internal definitions.

  • VS Code extension 0.6.5: definitions is colored as a block keyword before {, as indexes, checks, constraints and records are; a field named definitions keeps the color of a field name. The TextMate grammar takes definitions from BLOCK_KEYWORDS in parser/src/keywords.ts, and its test checks both readings. The extension skips 0.6.4, which changed no highlighting.

  • llms.txt and the MCP reference: a section on internal definitions, with the spec example, the targets that accept the block and the ones that take none, and three common mistakes: $defs instead of definitions, internal definitions for a relational target, and an internal definition named from another entity, which passes through silently as a target-native type. Item 13 of the checklist. mcp/src/reference.ts is regenerated from it, and every new example is checked against the parser.

Changed ​

Spec ​

  • Body keywords (§3.10): definitions is the seventh word that starts an element, only when { follows it, so a field may still be named definitions. Its entries form a list body (§3.9).

  • Scope (§15.5), field-level imports (§27.8), AST (§28), round-trip targets (§29.1) and lossiness boundary (§29.2): each takes internal definitions into account.

Not changed (compatibility) ​

  • Backward-compatible throughout. @xdbml/parse read no definitions block before 0.6.5, so no document that parsed before contains one.

v0.6.4 -- 2026 ​

Status: Draft -- superseded by v0.6.5 Released: 2026-10-02

A point release of the v0.6 draft that brings the parser in line with four rules the spec already stated, and checks every case of the grammar test corpus. A reader reported two cases of grammar/test-cases.md that the parser contradicted; running all of them against the 0.6.3 parser found thirteen. Documents continue to declare xdbml: 0.6.

Changed ​

Spec ​

  • Import item lists (§3.9, §27.2): the items of use { } and reuse { } form a list body, one per line or separated by commas, with commas and semicolons optional as in every list body. §27.2 always showed the multi-line form, without commas; §3.9 now lists it.

  • Tuple positions (§8.6): a gap, a repeated position or a first position other than 0 is an error in a document declaring v0.6 or later, and a warning in a document declaring an earlier version. The rule that positions form a contiguous range from 0 dates from v0.1; until v0.6.4 no parser checked it.

  • Named types under a built-in name (§15.2): Type varchar { ... } or Type int string is an error in a document declaring v0.6 or later, and a warning in a document declaring an earlier version. Built-in types always took precedence; until v0.6.4 no parser reported the declaration.

Tooling ​

  • Parser (@xdbml/parse): reads the items of an import list one per line, where it required a comma between two items ("Expected '}' closing import item list"). New diagnostics invalid-tuple-positions and named-type-shadows-builtin, with the severities above; the second compares names in any letter case, as built-in types match, so Type Money is reported against the built-in money. Refuses use and reuse in a document declaring xdbml: 0.1, since the module system belongs to the v0.2 feature set (Appendix D, item 3). Resolves a relative import next to an importer whose path uses Windows backslashes (C:\repo\models\main.xdbml), where it lost the importer's directory and read ./lib from the working directory; module keys and import cycles use forward slashes, which Node's fs accepts on Windows. Reads xdbml: 0.6.4.

  • Grammar test corpus: all 131 cases of grammar/test-cases.md now run in the parser's test suite, so npm run check fails when a case and the parser disagree. Three Ref cases and the polyglot case declare the entities they name; the module cases import from small files in grammar/fixtures/; the multi-line and single-line import forms are two cases, since importing one declaration twice into one document declares it twice; the tuple and built-in-name cases declare xdbml: 0.6, each beside a case declaring v0.5 that expects the warning; the case of a patch release newer than the parser declares xdbml: 0.6.99, a version no release reaches.

  • Example 06 (financial services): its Type Money becomes Type MonetaryAmount. Its four Money fields had the built-in money type all along, never the Type; they now reach it.

  • llms.txt: a Common mistake for a Type named after a built-in type, and the rule in the Fields section.

  • Playground help: the diagnostics panel page lists the two new codes and their severities.

Not changed (compatibility) ​

  • A document the 0.6.3 parser accepted parses with the 0.6.4 parser. A document declaring v0.6 with a gap in its tuple positions or a Type under a built-in name now draws an error; one declaring an earlier version, a warning.

v0.6.3 -- 2026 ​

Status: Draft -- superseded by v0.6.4 Released: 2026-10-01

A backward-compatible point release of the v0.6 draft. Chapter 18 defines diagram views, the subject areas or sub-models of a model's diagram: the elements a diagram view contains, how a listed Container combines with the entities named beside it, how names resolve, and what a renderer draws. A relationship appears in a diagram view when both of its ends do, so the Edges category is dropped. Every v0.6 document remains valid, and documents continue to declare xdbml: 0.6.

Added ​

Spec ​

  • Diagram view (§18): the chapter is rewritten, from one example and a list of categories into six sections. A diagram view lists entities under Tables, database views under Views, Containers under Containers, TableGroups, supertype groups and sticky notes, with * for every element of a category and { * } for every element of every category; a category left out or empty lists nothing (§18.1). A listed Container contributes all of its entities and database views, unless Tables names some of its entities or Views some of its database views, in which case it contributes only those named; a TableGroup or a supertype group contributes all of its members (§18.2). Names resolve as the endpoints of a Ref do: an unqualified name names the element of that name outside any Container, or else the one inside a Container, and is an error when several Containers hold one (§18.3). A renderer draws the frame of each Container and TableGroup around the members it holds, and each Ref and Edge whose two ends lie in members, and keeps positions and display options for each diagram view (§18.4). The DBML form, which Holistics added to DBML in April 2026, reads in every document (§18.5), and §18.6 lists the conditions a parser reports. Every condition is an error, except a category written twice in a document without a version declaration, which DBML accepts: there it is a warning.

  • Diagram view notes (§18.1): a diagram view takes a note, in the brackets after its name or as Note: in its body, and custom properties in the brackets; no other setting. Both are xDBML extensions, which need a version declaration (§18.5). §19 lists DiagramView among the constructs with inline notes.

  • AST (§28.8): a DiagramView carries its settings, its note and one Category node per category; its members are computed, not stored.

  • Conformance (§31): item 19, reading diagram views.

  • Appendix A: the DiagramView categories.

  • Appendix D: item 10, on the DBML form of DiagramView. Items 10 to 14 become 11 to 15.

Tooling ​

  • Parser (@xdbml/parse): reads DiagramView, where it raised "Unknown top-level construct", in every document, as a DiagramViewDeclaration with one category node per category, Schemas read as Containers, and a flag for the body-level { * }. An Edges category, an unknown category, and a DiagramView in a Container fail the parse with a message that cites §18.1. diagramViews() lists the diagram views of a document after module resolution, and diagramViewMembers() returns the members of one (§18.2) with the Containers, TableGroups and supertype groups drawn around them (§18.4). resolveNames() reports unresolved-diagram-view-name, ambiguous-diagram-view-name, diagram-view-wrong-category, duplicate-diagram-view and duplicate-diagram-view-category, unknown-diagram-view-setting for a setting other than note or a custom property, and construct-requires-version for Containers, Views or SupertypeGroups, a note, or settings in a document without a version declaration (§18.6). diagramViewNote() returns the note of a diagram view. reuse { diagramview X } imports the diagram view X, where it imported a database view of that name; the import is refused in a Container body, and a reuse * in a Container body moves an imported diagram view to the top level. The editor colors the category keywords before {. Reads xdbml: 0.6.3.

  • Grammar: diagramViewDefinition, which the grammar referenced without defining, with diagramViewCategory, diagramViewBodyItem and DIAGRAM_VIEW; a diagram view takes a settings block and notes. grammar/test-cases.md gains fifteen v0.6.3 cases, each checked against the parser.

  • Renderer (@xdbml/render): buildDiagram(doc, collapsedPaths, { diagramView }) and the diagramView option of renderToSVG draw a diagram view: its members, stacked again in their Containers' columns, a frame for each Container holding a member, and the relationships and Edges whose two ends are members. Field markers come from every relationship of the model, so an attribute keeps fk or dk when the other end is outside the diagram view, and every id stays the id it has in the full diagram. The interactive mount takes a diagramView option and gains getDiagramView() and setDiagramView(name, layout). diagramViewNames() lists the diagram views a document declares, for callers that refuse an unknown name.

  • Playground: a Diagram menu in the diagram toolbar, shown when the document declares diagram views, switches between Main ERD and each diagram view. Positions, Edge offsets, zoom, Display options and undo history belong to the diagram on display; collapsed rows are shared. A diagram view opened for the first time is arranged and fitted. A reload returns to the diagram view on display; renaming or removing it returns to Main ERD. A click on an empty area of the canvas selects the diagram on display, where it used to clear the selection and close the inspector: the inspector shows the Project on Main ERD (name, targets, counts of the model, note), or the diagram view (members, frames, categories as written, custom properties, note). A new help page, Diagram views, and additions to the pages on the diagram pane, the inspector, the diagnostics panel, parse failures and persistence.

  • Rendering API: a diagram_view query parameter, diagramView in a JSON body, renders one diagram view; a name the document does not declare returns 400 with the names it declares. The tester page gains a Diagram view field, and /health lists the option.

  • MCP server and llms.txt: render_xdbml takes diagram_view, refuses an undeclared name with the list of declared ones, and names the diagram view in its summary. validate_xdbml counts and names the diagram views, in its summary and its JSON tail. llms.txt gains a Diagram views section, a DiagramView row in What goes where (with its note and custom properties), a Common mistake for an Edges category, and DiagramView among the top-level declarations of the checklist; mcp/src/reference.ts is regenerated, and the MCP tests now load the renderer of the release from source as well as the parser.

  • VS Code extension 0.6.3 and the site's highlighting: the TextMate grammar colors the DiagramView categories before {, with the scope keyword.declaration.category.xdbml. The extension skips 0.6.2, which changed no highlighting.

  • Examples: example 16, Diagram views: subject areas, an order-to-cash model with three diagram views, each with a note. The §18.2 snippet with a supertype group gains a View in playground button.

Changed ​

Spec ​

  • Edges category removed (§18.1): v0.1 through v0.6.2 listed an Edges category. A Ref or an Edge appears in a diagram view when both of its ends do, so Edges is now an error.

  • Schemas category (§18.1): accepted as an alias of Containers, as in DBML.

  • Supertype group notation (§12.9): the subtypes connected to the symbol in a diagram view are the subtypes among its members.

Fixed ​

  • This changelog: the v0.6.1 heading, dropped in 0.6.2, is restored.

Not changed (compatibility) ​

  • Backward-compatible throughout. @xdbml/parse read no DiagramView before 0.6.3, so no document that parsed before contains one.

v0.6.2 -- 2026 ​

Status: Draft -- superseded by v0.6.3 Released: 2026-09-30

A backward-compatible point release of the v0.6 draft. Three DBML forms that the parser rejected or misread now parse as DBML reads them, and two rules the specification already stated are now reported. The AI reference, llms.txt, is rewritten from the specification, and CI checks every example in it. Every v0.6 document without a name collision or a Container target problem remains valid, and documents continue to declare xdbml: 0.6, though the parser now also reads xdbml: 0.6.2.

Fixed ​

Spec ​

  • Unquoted colors (§3.3): headercolor: #3498DB, the DBML form, reads as the string '#3498DB'. Tools that write xDBML quote it.
  • Aliases in relationships (§7.4): an alias names its entity in the endpoints of a Ref and of an inline ref:. An alias that repeats the name of an entity or a Container names nothing.
  • Target array types (§8.1): text[] and varchar(20)[], the DBML form of a PostgreSQL array type, keep their brackets in the type name.
  • Container targets (§5.2): rules 3 and 4 apply to Containers declared as such, when the Project declares targets:, and compare target names by their canonical form.
  • Name collisions (§15.5): two declarations of the shared namespace with one qualified name are an error, and a warning in a document declaring an earlier version than 0.6, or none.

Tooling ​

  • Parser (@xdbml/parse): reads #3498DB as a string where it raised a lex error, which rejected valid DBML. Resolves Ref: V.id ... and [ref: > V.id] through the alias of Table very_long as V, where it reported unresolved-entity; the key rule of §11.17 applies through the alias. Keeps [] in text[], where it dropped the brackets and read the field as text. Reports container-target-missing, container-target-not-in-project and name-collision; before, a Type and an Entity with one name replaced each other in the symbol table. Reads xdbml: 0.6.2.
  • Grammar: HEX_COLOR as a setting value, and ( LBRACK RBRACK )* after a scalar type. grammar/test-cases.md gains six v0.6.2 cases.
  • Renderer (@xdbml/render) and playground: a type such as varchar(20)[] displays as written rather than as varchar[](20). The help page on relationships gives its conceptual example the verb that reads in the direction it names.
  • MCP server and llms.txt: llms.txt is rewritten from the specification, with a table of what goes in the brackets and in the body of each construct, every field setting, the quoting and separator rules, the referenced side of each relationship operator, and a checklist. It gains Common mistakes for a missing comma between settings, a reversed relationship, a foreign key to part of a composite key, a block in a View body, a Ref in a Container, relationship settings beside an inline ref:, quotes and function defaults, types and settings from other languages, cardinality in the entity settings of an Edge, and a path across an array. The MCP tests now validate every example in llms.txt: a Right example with no diagnostic, a Wrong example with the diagnostic its comment names. mcp/src/reference.ts is regenerated; the server serves it once redeployed.

v0.6.1 -- 2026 ​

Status: Draft -- superseded by v0.6.2 Released: 2026-09-29

A backward-compatible point release of the v0.6 draft. List bodies accept a comma or a semicolon between items, as TableGroup and SupertypeGroup bodies already did, so Enum values and Entity fields written with commas, as AI assistants often write them, now parse. A field may take the name of any keyword: note, indexes, checks and records, which DBML accepts as field names, no longer fail. Chapter 14 states where each part of a View goes: the source query in the body, every other setting in the brackets. A key is declared once, and a unique key declared twice draws a warning. Every v0.6 document remains valid, and documents continue to declare xdbml: 0.6, though the parser now also reads xdbml: 0.6.1.

Added ​

Spec ​

  • Separators in list bodies (§3.9): in the body of an Entity, a TablePartial, an Edge, a View, an object-shaped Type, an object / struct / record, a json { } schema, a oneOf / anyOf / allOf, an Enum, constraints, checks, a DiagramView and its categories, and a Project, a comma or a semicolon may appear between items, before the first and after the last, with no meaning of its own. A parser accepts them in every document, including one without a version declaration (Appendix D, item 9). Tools that write xDBML write none. DBML accepts none in these bodies. indexes stays out: there, email, name may be meant as the composite index (email, name), so a comma between entries remains an error.

  • Keywords and field names (§3.10): no keyword is reserved as the name of a field or of an enum value. Note, indexes, constraints, checks, records and source_query start an element only when : or {, as the element requires, follows them. Before v0.6.1 the parser rejected a field named note, indexes, checks or records, which DBML accepts, and a View field named source_query.

  • Each key declared once (§10.3, §10.10): a key written inline is not repeated in constraints. A unique key declared twice, inline and in constraints or on two lines with the same fields in any order, draws a warning whatever version the document declares; a second declaration of the primary key was already an error (§10.4).

  • View validation summary (§14.7): a setting other than note written in the body is an error; source_query written in the brackets, and a second source query, are warnings.

  • View source query in the AST (§28.7): in the normalized AST, a View carries at most one source query, the first source_query: element of its body.

Tooling ​

  • Parser (@xdbml/parse): reads the separators of §3.9 in every list body, and reports a comma between index entries with an error that points to the composite form (email, name). Note, indexes, checks, records and source_query start their element only before : or { (§3.10), as constraints already did. A setting written in a body, such as materialized: true in a View, fails with an error that names the brackets, View monthly_revenue [materialized: ...] { ... }, instead of "Expected type name". viewSourceQuery() returns the source query of a View (§28.7), and resolveNames() reports source-query-in-settings and duplicate-source-query as warnings (§14.7), and duplicate-unique-key as a warning (§10.3). When a primary key is declared twice on the same fields, duplicate-primary-key names both declarations, as in "declares its primary key (customer_id) twice: [pk] on customer_id and a pk line in constraints", instead of reporting a second primary key. The playground editor colors a field named note like any other field name.

  • Grammar: listSeparator in every list body; replacement rules for tableDefinition, tablePartialDefinition, enumDefinition and tableGroupDefinition; fieldName, which accepts the six body keywords; and a View whose source_query is a body element only. grammar/test-cases.md gains fifteen v0.6.1 cases, each checked against the parser.

  • Renderer (@xdbml/render): no change of its own; it moves to the new parser at release. A new test checks that fields named note and records draw rows, and that a View's source query draws none.

  • Playground: the inspector shows a View's source query through viewSourceQuery(), the first source_query: of its body, where it joined every one before. The help pages on the diagnostics panel, the inspector, the editor and parse failures cover the two View warnings and duplicate-unique-key, a setting written in a body, list separators, and keywords as field names; the parse-failure page no longer says that a field named after a keyword needs quotes.

  • MCP server and llms.txt: both teach one field or enum value per line with no separator, the composite index form in indexes, keywords as field names, and the placement of View settings: in the brackets after the name, never in the body, with the source query as the one source_query: element of the body. mcp/src/reference.ts is regenerated; the server serves it once redeployed with 0.6.1. The MCP tests cover commas, a column named note, materialized in a View body and a bracketed source_query. llms.txt also gains a "Common mistakes" section: wrong and right forms, each checked against the parser, for a setting in a body, a key declared twice, a composite key written as several [pk], separators between items, and a comma between index entries. The 5-minute introduction states the same View placement under its example, and the "Use from AI assistants" page gains a section on giving llms.txt to an assistant without the MCP server.

Changed ​

Spec ​

  • §3.9 is reorganized around list bodies. The tuple, map, union and array rows keep their rules; the records row, whose rows end with their line, is new, and §3.1 notes that exception.

  • Chapter 14, View: §14.1 lists the body elements (source query, fields, note) and states that the settings of §14.5 go in the brackets. §14.2 shows materialized in the brackets, before the body, and shows the same setting in the body as a syntax error. §14.3 places the source query in the body; with more than one, the first counts. A source_query in the brackets, which the grammar of v0.6.0 accepted, is not the view's source query and draws a warning. §14.2 and §14.5 held two partial settings tables; §14.5 now holds the only one, without source_query.

  • §16 Enum: values are separated as in every list body, and a quoted value may use double or single quotes, as the parser already accepted; tools that write xDBML use double quotes.

  • §28, §31, Appendices A and D: aligned with §3.9, §3.10 and chapter 14. Conformance gains items 17 and 18; Appendix D gains item 9, and its later items are renumbered.

Fixed ​

Tooling ​

  • Parser: a field named note, indexes, checks or records, which DBML 3.13.6 accepts, failed to parse (for example with "Expected ':' or '{' after Note"), so a valid DBML file with such a column did not open in the playground and failed MCP validation. A View field named source_query failed the same way.

  • Parser: xdbml: 0.6.1 failed with "Unexpected token Dot", though §4 and the grammar allow MAJOR.MINOR[.PATCH]. The parser reads the patch number and supports documents up to 0.6.1: xdbml: 0.6 stays valid, and xdbml: 0.6.2 is refused as newer than supported.

  • Editor grammars: the TextMate grammar colored note varchar as a Note declaration, with the type colored as the declared name. Note is now a keyword only before : or {, or before a name and { on the same line, in the site's code blocks and in the VS Code extension 0.6.1, which packages the grammar. The playground editor gets the same fix through the parser (see Added).

Packages ​

  • @xdbml/parse 0.6.1 and @xdbml/render 0.6.1. The renderer has no change of its own; it moves to the new parser so the two stay in step. The MCP server and the rendering API are redeployed on them. The VS Code extension 0.6.1 is published to the Marketplace separately.

Not changed (compatibility) ​

  • Backward-compatible throughout. A document that writes source_query in the brackets, or declares two source queries, now draws a warning. A 0.6.0 parser rejects a document that uses commas or semicolons between list items, or a field named note, indexes, checks or records.

v0.6 -- 2026 ​

Status: Draft -- superseded by v0.6.1 Released: 2026-09-28

Adds constraints: one entity-level block for the primary key, unique keys and check expressions, each with an optional constraint name, with composite keys listed in key order and nested keys for document stores. A referential relationship must reference a key of the entity it points at. Every v0.5 document remains valid; documents using the new constructs declare xdbml: 0.6.

Added ​

Spec ​

  • Constraints block (§10.1): constraints { } in the body of an Entity, a TablePartial or an Edge. A line naming one field or a parenthesized list declares a key with pk or unique; a quoted line declares a check. constraints starts a block only when { follows, so a field may keep that name.

  • Keys (§10.2): name and note settings, fields listed in key order, and nested fields reached through objects; a key path never crosses an array, a tuple position or a map key. The index behind a key and whether the database enforces it stay with each target.

  • Inline and block declarations (§10.3): a key on one field without a name MAY stay inline as [pk] or [unique]; a named or multi-field key goes in constraints. [pk] on several fields still reads as one composite key, for DBML compatibility, but authors SHOULD NOT use it and tools that write xDBML MUST NOT produce it. [unique] on several fields declares separate keys. A pk entry in indexes reads as the primary key, with its name as the constraint name.

  • Primary key (§10.4): at most one per entity, declared in one place, with the conflict resolution of §17.1 for TablePartials. Its fields are not null in the AST whether or not the document says so, and null written on one of them is an error.

  • Quoted check expressions (§10.5): a check is written in single quotes, with backticks as an alias, in constraints, in checks { } and in the field-level check: setting. Elsewhere a quoted value stays a string. Tools that write xDBML switch to backticks when the expression contains a single quote.

  • Validation summary (§10.10): the new errors, with one rule on versions: in a document declaring an earlier version, or none, the conditions that concern constructs earlier versions accept (an index on an undeclared field, a second primary key, null on a primary key field, a relationship to a non-key) are warnings, so every valid v0.5 or DBML document stays valid.

  • Referenced keys (§11.17): the fields at the referenced end of a referential relationship must be the primary key or a unique key of the referenced entity (a DBML unique index counts), when that entity declares keys. Exempt: entities without keys, which a tool MAY give a primary key on import; many-to-many, foreign master and entity-level relationships; and entities with a non-relational target.

  • Relationship names as constraint names (§11.2): the name of a Ref is the name of its foreign key constraint in SQL targets.

  • Relational targets (§5.1): the targets subject to §11.17, with Db2, Db2 for z/OS and Teradata added to the target table.

  • Constraint nodes (§28.6): in the normalized AST, every key and check of an entity is a Constraint node, whichever form declared it, so the primary key sits in one place.

Tooling ​

  • Parser (@xdbml/parse): reads xdbml: 0.6, the constraints { } block (new AST nodes ConstraintsBlock and KeyConstraintEntry) and quoted check expressions (CheckEntry.delimiter records the form). entityConstraints(), primaryKey() and bodyConstraints() return every key and check of an entity, whichever form declared it (§28.6). In a 0.6 document each primary key field carries an implied not null (Setting.implied). resolveNames() reports the conditions of §10.10 -- invalid-key-flags, unresolved-key-field, key-path-crosses-collection, duplicate-primary-key, null-in-primary-key, duplicate-constraints-block, unresolved-index-field -- and ref-target-not-key for §11.17, the conditions on older constructs as warnings in documents declaring an earlier version or none. isRelationalTarget() exposes the list of §5.1.

  • Grammar: constraintsBlock, keyConstraint and a quoted checkEntry in grammar/xDBML.g4; grammar/test-cases.md gains ten v0.6 cases, each checked against the parser.

  • Editor grammars: constraints is highlighted as a block keyword in the playground editor, the site's code blocks and the VS Code extension's grammar copy. After the 0.6.0 release, the TextMate grammar also colors indexes, checks, constraints and records where they open a block in an entity body, which it had never done (the playground editor already did), and the VS Code extension 0.6.0 packages it: highlighting up to v0.6, target-native type names included. From 0.6.0 the extension's version follows the xDBML version its grammar covers.

  • Renderer (@xdbml/render): a pk line in constraints gives each field of the key the PK badge, like a pk entry in indexes, and a unique line on one field gives it the U badge. The fields of a composite unique key carry a numbered badge, U1 for the entity's first composite unique key, U2 for the second, including fields that are also in the primary key; a composite unique entry in indexes declares an index and stays unbadged. Primary key fields of a 0.6 document show the not-null marker, since the parser marks them not null.

  • Example 15, constraints: Formula 1 results as an Oracle schema and a MongoDB collection, with named composite primary and unique keys, quoted checks, named foreign keys, a composite foreign key that references a unique key, and a unique key on a nested field.

  • Playground: the entity inspector gains a Constraints section listing the primary key, unique keys and checks with their names, whichever form declares them, and counts primary key fields from any form. The help pages on the inspector, entity cards, visual cues and the diagnostics panel describe the constraints block, the badges it sets, and the conditions whose severity follows the declared version.

  • MCP server and llms.txt: both teach xdbml: 0.6, when a key goes inline and when in constraints (never several [pk] fields for a composite key), checks in single quotes, named foreign keys, and the rule that a foreign key references a key, with its exemptions. mcp/src/reference.ts is regenerated; the server serves it once redeployed with 0.6.0.

Changed ​

Spec ​

  • Chapter 10 is renamed from "Checks -- entity-level constraints" to "Constraints -- keys and checks" and rewritten in place, so no chapter is renumbered. The checks { } block of v0.2 remains valid and equivalent (§10.9); the former §10.3 to §10.5 are now §10.6 to §10.8.

  • §8.8, §9: the pk, unique and check: rows point to §10; the §9 example declares its primary key in constraints; every field an index names must be declared.

  • §3.5, §4.1, §11.4, §11.11, §29.2, §31, Appendices A, B and D: aligned with §10 and §11.17. Conformance gains items 13 and 14.

Fixed ​

Examples ​

  • 08-university-registrar and 10-modules-consumer: declare xdbml: 0.6 and move their composite primary keys from [pk] on several fields to constraints (§10.3). Example 08 names its keys and adds a capacity check; example 10 moves its line-total check into constraints and writes its field-level checks in single quotes.

  • 05-healthcare-fhir: the patients indexes named family_name_lookup, which is not a field of the entity. The index check of §10.10 reports it; the entry is now an expression index on the first family name.


v0.5.1 -- 2026 ​

Status: Draft -- superseded by v0.6 Released: 2026-09-28

A backward-compatible point release of the v0.5 draft. The parser accepts any type name, as §1.2 always stated, reads container-qualified Enums in their DBML form, and warns about a type name that looks like a misspelled Type or Enum; editors color the common types of each target. §16 gains a clarification and an example. Every v0.5 document remains valid, and documents continue to declare xdbml: 0.5.

Added ​

Tooling ​

  • Target-native type names colored: TARGET_NATIVE_TYPES in parser/src/keywords.ts lists 72 common type names of specific targets: Oracle number, clob and raw, PostgreSQL serial, bytea and citext, SQL Server uniqueidentifier and datetimeoffset, Snowflake timestamp_ntz, BigQuery float64, the integer types of Cassandra, Protobuf and ClickHouse, and geometry, geography and interval. The playground editor and the site's code blocks color them like scalar types, and so will the next build of the VS Code extension, whose grammar copy is updated. They are not built-ins: the resolver passes them through like any other target-native name, and a Named Type may be declared under one of them, whereas a name in SCALAR_TYPES is a built-in that no Named Type can shadow (spec §15.2). Generic words that often name fields (point, line, box, path, image, duration) are left out, since coloring does not depend on position. The parser's keyword-consistency tests and the TextMate smoke test cover the list.

Changed ​

Spec ​

  • §16 Enum, clarification: the section already allowed container-qualified Enums; it now shows both ways to declare one, inside a Container block or under a qualified name, states that the two forms are equivalent, so declaring one Enum both ways is a duplicate, and shows a field naming each by its qualified name. The example has a View in playground button (114 buttons across v0.1 to v0.5).

  • Adjacent standards, editorial update (no change to the language): Open Semantic Interchange is now Apache Ossie (incubating), with its new name and repository link in the abstract, §1.1, §24, §30 and the references; §30 gains a LinkML row and a sentence on how the two languages differ, and LinkML joins the references. The site pages (FAQ, README, home page, xDBML in 5 minutes, ecosystem, governance, contributing) follow, and the FAQ gains "How does xDBML compare with LinkML?".

Tooling ​

  • Playground help, Diagnostics panel: the page described warnings as future work and said the parser recovers to report several syntax errors at once. It now describes the two stages as they run: parsing stops at the first syntax error and the diagram keeps the last good state, while resolution reports errors and warnings together and the diagram and inspector keep working. It lists the four warnings (possible-type-typo, ambiguous-ref-endpoint, empty-supertype-group, merge-without-roll-up), the row layout with its code, and the default expanded state. The Editor pane page mentions the target-native type colors, and comments in the playground source that still called warnings a future feature are updated.

  • MCP server and llms.txt: the paragraph on types now says that any other type name is valid and passes through as written, so a physical model uses its target's own types (number(10), varchar2(255), serial, bytea); it shows how a field names an Enum declared in a schema (status core.job_status) and names the possible-type-typo warning. mcp/src/reference.ts is regenerated from it; the MCP server serves the new text once redeployed.

Fixed ​

Parser (@xdbml/parse) ​

  • Target-native type names: the name resolver reported unresolved-type for every scalar type name missing from the parser's highlighting lists, among them Oracle number, clob and raw, PostgreSQL serial and bytea, SQL Server uniqueidentifier, and the number(10) of the §27.16 field-level import example. A field typed by a declared Enum, which DBML allows, failed the same way. Scalar type names pass through as written (spec §1.2, principle 4), so a name that is neither a builtin nor a declared Type or Enum is now accepted as a target-native type with no diagnostic. The exception is a near miss of a declared Type or Enum -- a difference of case, or one or two edits depending on the length of the name -- reported as the new possible-type-typo warning, with the declared name as the suggestion. unresolved-type stays in DiagnosticCode, but the field-type pass no longer emits it for a scalar name. A path that navigates into a target-native or Enum-typed field reports invalid-nested-path, as a path into a builtin scalar already did.

  • Container-qualified Enums: the parser rejected enum core.job_status { ... }, the DBML form of an Enum declared in a schema, and a field type naming it, status core.job_status, although §16 allows container-qualified Enums. Both now parse, with quoted segments allowed ("billing"."invoice status"). The resolver files an Enum declared under a qualified name in that container, as if it were declared in the Container block, so the two forms share one qualified name and declaring both is a duplicate-declaration. A qualified type name that names no Enum, such as public.geometry, passes through as a target-native type, and a near miss of a qualified Enum suggests the qualified name. use { enum core.job_status } imports such an Enum under its bare name, as it does a container-scoped one. The grammar gains a typeName rule under scalarType, and grammar/test-cases.md gains a valid and an invalid case.

Packages ​

  • @xdbml/parse 0.5.1 and @xdbml/render 0.5.1. The renderer has no change of its own; it moves to the new parser so the two stay in step. The MCP server and the rendering API are redeployed on them.

Not changed (compatibility) ​

  • Backward-compatible throughout. A document that raised unresolved-type for a scalar type name now resolves cleanly, unless the name is a near miss of a declared Type or Enum, which raises a warning rather than an error. unresolved-type remains in DiagnosticCode.

v0.5 -- 2026 ​

Status: Draft -- superseded by v0.5.1 Released: 2026-09-21

Adds supertype groups: generalization of entities into a supertype and subtypes, with inherited attributes and the intended materialization. Every v0.4 document remains valid; documents using the new construct declare xdbml: 0.5.

Added ​

Spec ​

  • Supertype group (§12): a new top-level SupertypeGroup declaration, placed as chapter 12 ahead of Edge. A group names one supertype in its settings and lists its subtypes in its body, separated like TableGroup members (§12.1, §3.9). The name is required; a writer exporting a group that has no name in its source tool emits undefinedGroup1, undefinedGroup2, and so on.

  • Completeness and exclusivity (§12.3): completeness: total | partial and exclusivity: disjoint | overlapping, set per group, so one supertype can carry a total, disjoint axis and a partial, overlapping one. Absence means unstated, as with constraint_type.

  • Value aliases (§12.2): each canonical value accepts the spellings of other tools and of ORM frameworks (complete, incomplete, exclusive, non_exclusive, class_table, joined, single_table, concrete_table, table_per_class, flat_with_discriminator). The normalized AST holds the canonical value, and writers emit it.

  • Hierarchies (§12.4): several levels, several axes per supertype, and single inheritance on the subtype side -- an entity listed as a subtype in two groups is an error. Cycles, self-subtyping, and duplicate members are rejected.

  • Inherited attributes (§12.5): a subtype declares only its own attributes. The attributes of its supertypes apply to its instances without being copied or declared again, and their placement in stored structures is left to derivation. Redeclaring an attribute of a supertype is an error. A path through a subtype names its own attributes only, and a relationship that points at a subtype names it as an entity-level endpoint.

  • Identity (§12.6): a subtype without a primary key shares the identity of its supertype, and a subtype may declare a key of its own. The key each stored structure receives is derivation output.

  • Materialization intent (§12.7): strategy: preserved_hierarchy | roll_up | roll_down, merge: flat | nested for roll-up, and discriminator, which reuses the keyword of §21.2 and is rejected on an overlapping group. A subtype member may carry its own strategy, which takes precedence for that pair. The settings record intent for a derivation tool and add nothing to the document's structure.

  • Validation summary, diagram notation, relationship to other constructs, and Hackolade Studio interchange (§12.8 to §12.11), including the half-circle notation: rounded side toward the supertype, a cross for disjoint, a bar just above the base for total, and dashed marks for unstated settings, with a figure of the five variants (/diagrams/supertype-group-notation.svg).

  • Diagram View (§18): a SupertypeGroups category.

  • Module system (§27.3): supertypegroup is an importable element type.

  • AST (§28): a SupertypeGroup node with Subtype children. Supertype chains are computed for the structural and redeclaration checks rather than stored (§28.5); no attribute or key of a physical model is computed.

  • Scope (§1.1): physical derivation listed as out of scope, with xDBML recording intent where a construct carries one.

  • View in playground buttons (§12, Appendix C.5): seven snippets open in the playground. A fragment opens with an xdbml: 0.5 line and empty declarations of the entities its groups name, after a marker comment, so the diagram has something to draw; the §12.4.3 snippet opens on the diagnostic it illustrates. The snippets shown in the spec are unchanged.

  • Appendix C.5: a worked example with two axes, three levels, a per-subtype strategy, and a relationship that points at a subtype.

Tooling ​

  • scripts/spec-playground-links.mjs: checks that every "View in playground" button in spec/vN.M.md opens the snippet below it (113 buttons across v0.1 to v0.5 at this point) and, with --write, regenerates the ones that do not, including a new button written with an empty #s=. Runs as npm run check:spec-links, in npm test, and in CI. Arguments that are not .md files are ignored with a warning, since npm test passes its own extra arguments to this last script in its chain.

  • Parser: SupertypeGroup parses to a SupertypeGroupDeclaration with SupertypeGroupMember children, each member carrying its own settings. Members are separated like TableGroup members. A group without a name is a parse error. supertypegroup is a selective-import element type and survives aliasing, clone blocks and flatten(). The keyword joins DECLARATION_KEYWORDS, so the playground editor highlights it.

  • Supertype group checks (spec §12.8), run by resolveNames() on the flattened document so the playground and the MCP validate_xdbml tool report them: missing-supertype, unresolved-supertype-group-member (unresolved, ambiguous across containers, or naming a View, Edge, Type or other non-entity), invalid-supertype-group-value, duplicate-subtype, supertype-is-subtype, subtype-in-multiple-groups, supertype-cycle, supertype-attribute-redeclared (including attributes arriving through a TablePartial, on either side), discriminator-on-overlapping-group, and the warnings empty-supertype-group and merge-without-roll-up. Duplicate group names reuse duplicate-declaration; a group in a document declaring less than 0.5 reuses construct-requires-version.

  • @xdbml/parse exports supertypeGroupSettings() and subtypeStrategy() (canonical values), canonicalSupertypeGroupValue() and SUPERTYPE_GROUP_VALUES (the alias table), resolveSupertypeGroups() (members resolved to entity ids), supertypeChains() (nearest supertype first), and checkSupertypeGroups(). None of them computes the attributes or keys of a physical model.

  • Grammar: supertypeGroupDefinition and its settings and member rules in grammar/xDBML.g4, with a SEMICOLON token; ten cases in grammar/test-cases.md, each verified against the parser.

  • Renderer: supertype groups draw per spec §12.9. DiagramModel.supertypeGroups carries each group resolved against the canvas, with members resolved by the parser's resolveSupertypeGroups() so the diagram and the diagnostics agree. Geometry is computed at draw time from current bounds, so it follows a drag: a stem from the supertype's bottom edge to the top of a half-circle, a cross for disjoint and a bar for total, each dashed when unstated, and one line from the base to a bus with an orthogonal drop to each subtype. Subtypes are expected below the supertype in this first phase. Several groups on one supertype leave its bottom edge at evenly spaced points, in declaration order, with staggered buses. A relationship line attaching to an edge a group uses moves near the left corner. @xdbml/render exports layoutSupertypeGroups(), supertypeGroupAnchorX(), SYMBOL_RADIUS, SYMBOL_STEM and the SupertypeGroupLayout and SupertypeGroupGeometry types.

  • Relationship names: a showRelationshipNames render option, and a relationshipNames visibility key in the interactive mount, draw the name of a named Ref at the middle of its line's longest segment and each supertype group's name beside its symbol. Off by default, so existing diagrams are unchanged.

  • Interactive mount: a click on a group symbol selects { kind: 'supertypeGroup', id }; the selected group's symbol and branches are highlighted. RelationshipVisibility gains supertypeGroups (default shown) and relationshipNames (default off).

  • Auto-arrange: in the relational strategy, each supertype hierarchy is placed first, supertype above and centred over the subtypes of all its groups, levels stacked, with extra row spacing for the symbols; the rest of the component is arranged around it. Supertype-subtype pairs count as links, so a hierarchy with no relationship lines stays together. The star strategy is unchanged.

  • MCP server and llms.txt: both teach xdbml: 0.5 and supertype groups: when to use a group, one group per axis, subtypes declaring only their own attributes, completeness and exclusivity, the materialization settings as derivation intent, and pointing a relationship at a subtype as an entity. mcp/src/reference.ts is regenerated from public/llms.txt. The MCP tests run against the parser source through the same test-only hook as the renderer and playground tests, with three new cases: a valid group document, group rules surfacing as diagnostics, and a newer version refused.

  • Editor grammars: the TextMate grammar and the VS Code extension highlight SupertypeGroup and the setting keys supertype, completeness, exclusivity, strategy and merge. Regenerating the extension's copy also brings in foreign_master and constraint_type from v0.4, which had not been copied into it. No new .vsix is built.

  • Playground: a supertype group inspector opens when a group symbol is clicked. It shows the supertype and the subtypes as links, completeness and exclusivity ("Unstated" when absent) read back as a sentence such as "Every Party is exactly one of: Person, Organization.", each subtype's own strategy or "follows the group", the materialization intent (strategy, merge, discriminator), remaining settings, and the note. The entity inspector gains a "Supertype groups" section: a supertype lists each group it anchors with that group's subtypes, a subtype lists its group and its supertype, and an entity that is both shows both. Every group and entity name in either pane selects it, and the diagram selection follows. The Display menu gains "Supertype groups" under Relationships and a "Labels" heading with "Relationship names" (off by default); both persist like the other toggles, and the label option does not count as something hidden.

  • Playground help: a new "Supertype groups" page under Reading the diagram, with the five symbols drawn inline, how groups arrange and hide, the group inspector, and the diagnostics you may meet. The pages that had not caught up with v0.4 now cover it: the relationships page explains line styles (solid foreign key, dotted foreign master, open circles for inactive), the fk/dk/fm/dm markers, conceptual relationships with their direction triangle, roles and verbs read in both directions, identifying and non-identifying constraint types, relationship names and the Display menu; the entity-cards and visual-cues pages list the badges the diagram actually draws (P, U, !, fk, dk, fm, dm; the AUTO badge they described does not exist); the inspector page covers the relationship reading, the entity's supertype groups section, the group inspector and links between panes; the diagram-pane page describes the Arrange and Display menus. Stale statements corrected on the way: relationship lines are orthogonal rather than curves, Open and Save exist, a DBML document parses as it is, fourteen examples are bundled, and warning-level diagnostics are no longer a roadmap item.

  • Example 14, supertype groups: the Appendix C.5 model, with three groups over two axes and three levels, a per-subtype strategy, and relationships to a subtype's own key and to a subtype as an entity-level endpoint. Its golden SVG is new; the thirteen existing goldens are byte-identical.

Changed ​

Spec ​

  • Chapter numbering: the new §12 moves every chapter from Edge onward up by one (Edge §13, View §14, ... Conformance §31). All cross-references inside v0.5 follow. Earlier versions keep their own numbering.

  • Conversion to DBML is no longer specified: xDBML is a superset of DBML that DBML parsers are not expected to read, and the specification does not govern DBML output. §29.1 lists DBML in the DBML → xDBML direction only and drops the sentence about emitting xDBML-only constructs as comments when downgrading. §29.2 no longer lists foreign master relationships as lossy to DBML. Appendix D items 10 and 11 of v0.4, which instructed a generator writing DBML, are replaced by one item stating that the specification defines no conversion from xDBML to DBML.

  • §4.1: xdbml: 0.4 is parsed by 0.4+ parsers; xdbml: 0.5 is refused by a 0.4 parser.

  • §3.9 and §12.1: SupertypeGroup members are separated like TableGroup members, by newline, comma or semicolon. The separators table lists TableGroup for the first time; the parser has always accepted those separators there.

Examples ​

  • Section references follow the v0.5 numbering in the comments and notes of examples 02, 09, 10 and 11 and in the example descriptions of scripts/examples-manifest.mjs. The reference in example 02 to the Decimal128 lowering pointed at the wrong chapter and now names §23.2.

Tooling ​

  • The parser refuses a newer version (spec §4.1): a document declaring a version above SUPPORTED_XDBML_VERSION (0.5) fails with a ParseError whose new optional code is unsupported-version. Until now a parser accepted any declared version silently. Documents declaring 0.1 to 0.5, and DBML documents with no declaration, are unaffected. compareVersions() is exported.

  • Section references in parser comments and test names follow the v0.5 numbering. References already pinned to a version, such as "v0.2 §26", are unchanged; references that pointed at an earlier numbering (Named Type as §13, View as §12, Records as §24, path syntax as §18) now name the right chapter.

  • Renderer and playground tests run against the parser source: a test-only module hook (test/parser-from-source.mjs in renderer/ and playground/) resolves @xdbml/parse to parser/src, as the playground's Vite alias already does. A renderer change that depends on a parser change in the same release can then be tested before the parser is published. The published packages resolve @xdbml/parse normally.

  • Parser tests parse the repository's examples: the suite now reads examples/ rather than private copies under parser/test/examples, which had drifted from the published files and stopped at example 11. All fourteen examples are parsed; the copies are removed.

  • Playground help in the site's Learn menus: the top-level Learn menu and the "Learn xDBML" sidebar groups link the playground help (Getting started). Until now the help pages were reachable only from the playground's Help button.

  • Site shows v0.5 as the current draft: the specification index, the three specification menus in .vitepress/config.ts, and the README list v0.5 as current and v0.4 as superseded. /spec/current follows from scripts/prepare-spec.mjs with no change. The FAQ reference to the module system names §27.

Fixed ​

Spec ​

  • §3.9: the subsection on element and field separators was numbered 3.8 a second time; it is now 3.9.

v0.4 -- 2026 ​

Status: Draft -- superseded by v0.5 Released: 2026-09-20

Expands relationships with a second relationship type. Every v0.3 document remains valid; documents opting into the new construct declare xdbml: 0.4.

Added ​

Spec ​

  • Foreign master relationship (§11.10): a Ref may carry a foreign_master flag, which records where a duplicated attribute of denormalized data is mastered. A Ref without the flag is referential -- the foreign key xDBML has always expressed. The flag is available in the short, long, and inline declaration forms.

  • foreign_master beside an inline ref: (§11.10.2): the one exception to the rule that settings are not supported on inline ref: declarations. A field holds at most one inline ref:, so the flag has exactly one relationship to qualify.

  • Foreign master restrictions (§11.11): single-attribute endpoints (the composite form of §11.4 is rejected); at most one master per child attribute; no key requirement on either endpoint; and no output from any generator. Path syntax, cardinality, and every other relationship setting follow the referential rules unchanged.

  • Derived attribute roles (§11.12): a normative table for fk, fm, dk, and dm. An implementation computes all four from the set of Ref declarations; no document declares them.

  • Relationship line styles (§11.13): a normative table pairing each relationship type with a line style, plus the recommendation that a renderer offer a toggle hiding foreign master relationships.

  • Roles and verbs (§11.14): source_role / target_role name the role the entity at each end plays, and source_verb / target_verb carry the verb that reads the relationship from that end. Documentation only; no generator emits them. Available on an Edge as well.

  • Constraint type (§11.15): constraint_type: identifying | non_identifying records whether the child's foreign key participates in the child entity's primary key. It changes no keys and is not derived from them, so it can be stated before the child has a primary key. Absence means unstated, which is distinct from non_identifying. §11.15.1 maps the property onto the spellings used by Hackolade Studio, erwin, ER/Studio, and PowerDesigner, whose CDM and LDM call it a Dependent checkbox set per direction.

  • Entity-level relationship endpoints (§11.16): a Ref endpoint may name an entity rather than an attribute, so a relationship can be drawn between two concepts before either has attributes and then refined in place without changing declaration kind. Such a relationship states no cardinality (§11.8 inference does not apply), produces no fk / fm / dk / dm markers, and takes its reading direction from the operator alone, with - meaning linked without a stated direction and <> rejected. undirected: true marks a relationship that reads the same way from both ends, matching undirected on an Edge.

  • Settings block on the long Ref form (§11.2): the long form now accepts a settings block after the relationship expression inside the braces, which puts every setting of §11.9 in all three declaration forms. A long form written without a settings block parses as before.

Tooling ​

  • Parser: constraint_type, source_role / target_role, source_verb / target_verb and undirected are recognized settings. constraint_type takes identifying or non_identifying and is rejected on a foreign master; undirected takes true or false; <> is rejected between entities; the documentation settings are gated on xdbml: 0.4 like the foreign_master flag. New diagnostics: invalid-constraint-type, constraint-type-on-foreign-master, invalid-undirected, entity-level-many-to-many, ambiguous-ref-endpoint. @xdbml/parse exports constraintType, isUndirected, entityNames and isEntityLevelEndpoint.

  • Renderer: entity-level endpoints resolve and draw, anchored to the entity boxes. No cardinality is inferred for them, and a direction arrow replaces the crow's foot. @xdbml/render exports collectRefDeclarations, one definition of the order the diagram numbers relationships in. RefLayout carries relationshipType, inactive, entityLevel, undirected and decl.

  • Playground: the relationship inspector shows type, direction, constraint type, and the roles, verbs and cardinalities, with the relationship read back as a sentence. The Display dropdown gains a Conceptual toggle alongside foreign key, foreign master and inactive.

  • Example 12, conceptual to denormalized: one order-management model holding an entity-level relationship, foreign keys carrying roles, verbs and a constraint type, and five foreign masters including one crossing an array.

  • Example 13, denormalization with foreign master: a storefront order document with eight foreign masters across both declaration forms, including copies inside array elements, and a deliberate counter-example (unitPrice is not a foreign master, because the price charged may differ from the catalogue price).

  • MCP server and llms.txt: both teach xdbml: 0.4 and the new constructs. CI now fails when mcp/src/reference.ts drifts from public/llms.txt, the cheatsheet it is generated from.

  • tools/RELEASE.md, tools/release.cmd, tools/release-preflight.cmd: the release is now a script rather than a list of manual edits. Preflight checks npm login, whether the version is already taken, wrangler auth, missing dev tooling, NODE_ENV=production, generated-file drift and the test suites, and changes nothing. The release script bumps every version and dependency range through npm version and npm pkg set so package.json and package-lock.json move together, waits for each package to become visible on the registry before the next step depends on it, and stops at the first failure. RELEASE.md carries the same sequence by hand, plus the expected-but-harmless output and the genuinely wrong output, separated.

  • tools/github-release.cmd and scripts/release-notes.mjs: tagging and publishing the GitHub release is a command rather than a web form. Notes are extracted from the ## v<version> section of CHANGELOG.md, so nothing is retyped and the release cannot drift from the repo. The script refuses a dirty tree or a HEAD that differs from origin/main, is safe to rerun, and falls back to writing the notes file and printing the URL when the GitHub CLI is absent.

  • MCP SERVER_VERSION now reads package.json instead of restating the version in src/index.ts, removing a second place to bump that could drift.

Fixed ​

Tooling ​

  • Inline refs on nested fields are drawn. An inline [ref: ...] written on a field inside an object or array element was silently dropped by the renderer, with no diagnostic and no line. Where an endpoint pointed was never the problem; where the setting was written had to be a top-level field.

  • The inspector resolves a relationship that came from an inline ref. The renderer numbered top-level and inline relationships in one sequence while the inspector counted RefDeclaration statements alone, so a line from an inline ref opened an empty pane. Both now index into collectRefDeclarations.

Changed ​

Spec ​

  • inactive line style (§11.9): an inactive relationship renders as a line of small open circles rather than a dotted line, which frees the dotted line for foreign master relationships and matches Hackolade Studio. The change affects rendering only; the flag's meaning is unchanged.

  • §22.2 cross-engine references: the value-space compatibility table extends to foreign master relationships, at warning level, since a mismatch there reaches no generator.

  • §27 AST, §28 round-trip, §30 conformance, Appendix A, Appendix D: updated for the new flag. Foreign master relationships are lossy to DBML and to every engine DDL target by design; a generator writing DBML omits them rather than emitting a Ref a downstream tool would read as enforceable structure.

v0.3.1 -- 2026 ​

Status: Draft -- superseded (point release of v0.3) Released: 2026-07-03

A backward-compatible point release of the v0.3 draft: one small surface-syntax addition, spec clarifications, and tooling fixes across the parser, renderer, playground, and MCP server. Every v0.3 document remains valid, and documents continue to declare xdbml: 0.3.

Added ​

Spec ​

  • Array/list/set element-type shorthand (§8.4): array [T1, T2, ...] is now shorthand for array [union [T1, T2, ...]], and list and set behave the same. The listed members must be the types a union accepts (scalar or named types, or null) and must be comma-separated -- the comma is what distinguishes the shorthand from the named-element form array [name type]. This lets a schema describe the heterogeneous arrays JSON and BSON allow without writing union. A single element type is unchanged, and the positional tuple form is unaffected.

  • Element and field separators table (§3.8): a normative table of which separator joins the items inside each construct -- newline-only for entity/edge/object/oneOf bodies, comma-and/or-newline for tuples, comma-required for map and union, and the element-type shorthand for array/set.

  • "View in playground" links in the spec: every complete, renderable example now carries a link that opens it in the interactive playground.

Changed ​

Spec ​

  • §8.4 "Array of scalars" rewritten: clarifies that an array/set holds a single element type expression (which may itself be polymorphic), that JSON/BSON arrays need not be uniform, and points to union (scalar mix), oneOf (object-shape mix, §20.5), a tuple (fixed positions, §8.6), or an opaque json field for each kind of heterogeneity.

  • §8.6 tuple separators clarified: tuple elements may be separated by a comma, a newline, or both -- the separator is optional and position-independent.

Grammar ​

  • Conformance pass on the example corpus: object/struct bodies in grammar/test-cases.md and in the spec examples that used inline comma-separated fields (which the formal .g4 does not permit) were rewritten to the newline-separated form the grammar defines, so the corpus is self-consistent with the grammar and the parser.

  • arrayType / setType: extended to accept the comma-list element-type shorthand.

Fixed ​

Parser (@xdbml/parse) ​

  • Newline-separated tuple elements: a heterogeneous tuple whose elements are separated by newlines rather than commas now parses. Previously the parser required a comma between elements and greedily consumed the next [N] position marker as a settings block. Comma-separated tuples and per-element settings are unchanged.

Renderer (@xdbml/render) -- dark mode ​

  • Selected-row legibility: a selected field row is no longer washed out in dark mode; the selection fill and accent strip are now theme tokens, so the row keeps light text on a dark tint.

  • Footer link and selection outlines: the standalone-SVG "Open in playground" footer link and the entity/container/relationship selection outlines are now theme-aware and stay legible on the dark canvas. Light-mode output is unchanged.

Tooling ​

  • Playground dark mode: a light/dark toggle with persistence, ?theme= hand-off from the documentation site, and protection against Chrome's automatic dark-theme re-coloring.

  • MCP render_xdbml mode parameter: the render tool accepts mode: "light" | "dark"; in dark mode the returned SVG and PNG carry a matching dark backdrop so the diagram is legible in any viewer.

Packages ​

  • @xdbml/parse 0.3.2 (tuple fix, array/set shorthand) and @xdbml/render 0.3.1 (dark-mode theming) were published; the MCP server was redeployed on @xdbml/render 0.3.1.

Not changed (compatibility) ​

  • Backward-compatible throughout. Every v0.3 document remains valid; the element-type shorthand is purely additive surface syntax that desugars to the existing union type, and requires no change to a document's xdbml: version.

v0.3 -- 2026 ​

Status: Draft -- superseded by v0.3.1 Released: 2026

Added ​

Module system ​

  • Remote module sources (§26.14): a use or reuse directive may now import a module from an https:// URL in addition to a relative path. The source form is recognized purely by scheme. A remote source changes only where a module's bytes come from, not what an import does: both directive keywords, both selection forms (the import-all * and the selective { ... } list), per-symbol as aliasing, and directive settings behave identically whether the string after from is a path or a URL. This delivers the URL imports deferred in v0.2.

  • HTTPS-only, with a strict scheme boundary (§26.14): only the https scheme is permitted. A source using http, file, git, ssh, data, a protocol-relative //host/path, or a bare host is rejected with a located error and never reinterpreted as a relative path. This keeps the local/remote boundary unambiguous and refuses plaintext transport for content the importing document will trust as schema.

  • Relative references inside remote modules (§26.14.1): a relative source inside a remote module resolves against that module's base URL per RFC 3986, never against the entry document or any filesystem location. An author can publish a multi-file module set, expose a single entry file, and rely on the set's internal references continuing to work once imported by URL. A consequence is a deliberate trust boundary: a remote module can never reach the importing system's filesystem.

  • Browser-based resolution and CORS guidance (§26.14.6, informative): notes on retrieving remote modules from browser-based tooling.

Tooling -- interactive playground ​

  • Interactive playground: a live editor-and-diagram environment pairing a Monaco source editor (with xDBML syntax highlighting and code folding) with a live-rendered ERD and a properties inspector. Schemas are shareable via URL, and the example pages and landing pages link directly into the playground.

  • ERD auto-arrange: relational and star-schema layout strategies. A relational arrangement is applied automatically the first time a document is opened, framed to fit the pane.

  • ERD direct manipulation: entities and property-bearing edge boxes can be dragged to reposition, layout is persisted per document, layout changes have undo/redo (keyboard and floating-bar buttons), and zoom controls offer fit and reset.

  • Inspector: shows properties for containers, entities, attributes, relationships, and edge boxes.

Tooling -- renderer package ​

  • New @xdbml/render package: a framework-free SVG renderer for xDBML diagrams (pure layout, geometry, a string serializer, and an interactive DOM mount), extracted from the playground so diagram rendering has a single source of truth. It is reusable by outside systems and is the shared core for a planned rendering API service and MCP server.

  • Playground migrated onto @xdbml/render: the playground now renders and handles all canvas interaction through the shared mount; the duplicated layout engine and the per-component diagram renderers it replaced were removed.

  • @xdbml/parse and @xdbml/render packaged for external consumption: both build to dist with declaration output and an exports map, so they can be consumed outside the monorepo (the prerequisite for the API service and MCP server).

Examples ​

  • Examples 02 and 10 extended to demonstrate complex Type reuse.

Changed ​

  • Conformance (§30): v0.3 implementations parse v0.1, v0.2, and v0.3 documents with their respective semantics, and support remote module sources per §26.14.

Not changed (compatibility) ​

  • Every v0.2 document remains valid under v0.3. Remote module sources are a strict, backward-compatible addition.
  • Relative-path imports resolve exactly as in v0.2.
  • v0.1 documents and DBML 3.13.6 documents (no version directive) continue to parse with their respective semantics.

v0.2 -- 2026 ​

Status: Superseded by v0.3 (still supported) Released: 2026

Added ​

Module system ​

  • Module system (§26): use and reuse directives for importing declarations from other xDBML or DBML files. Strict superset of DBML's module system, extended to cover xDBML-specific constructs (Container, Entity, Collection, Record, Type, Edge, View, DiagramView, TablePartial, Enum, Note, TableGroup) plus field-level imports.

  • Clone blocks (§26.6): optional inline embedding of imported content. A clone block following a use/reuse directive captures the imported declarations directly in the importing file, so the file parses correctly even when the referenced file is unavailable. Tools manage clone refresh; drift detection is out of spec scope.

  • cloned_at metadata setting (§26.6): an ISO 8601 timestamp recording when a clone was captured. Informational; the parser does not act on it. Tooling may use it to drive refresh workflows.

  • Field-level imports (§26.8): reuse { field X.Y.Z } from ... clones a single field declaration as a reusable named shape. The declaration sits at file scope; placement happens by using the imported name as a field's type elsewhere, matching scalar Named Type behavior.

  • Container-scoped imports (§26.5): use/reuse directives may appear inside Container bodies for non-field element types. The directive's location determines where the imported element lives in the merged AST. An entity imported into Container ordering is named ordering.products, not core.products.

  • from, use, reuse, as as reserved keywords in directive positions (Appendix A).

  • Cross-platform path resolution guidance (§26.13): relative paths with ./ and ../ prefixes use forward-slash separators on all platforms. Browser-based renderers reading referenced files via File System Access API may encounter permission prompts; clone blocks eliminate this concern by making files self-contained.

Type system ​

  • Scalar Named Types (§14.7): Named Types extended to support scalar shapes carrying the full field-level surface (validation constraints, notes, AI-readiness metadata, custom properties). Type Email varchar [pattern: '...', tags: ['pii']] is now valid alongside the existing object-shaped form.

Entity-level constraints ​

  • Checks block (§10, NEW SECTION): entity-level checks { } block declaring multi-column constraint expressions. Each check is a backtick-wrapped expression with optional name: and note: settings. The block is a peer of the indexes { } block. Required for full DBML 3.13.6 compatibility -- DBML documents with checks { } blocks now parse correctly without semantic loss. Inserting this section between §9 Index and the former §10 Relationship caused all subsequent sections to shift by one.

Relationship visualization ​

  • inactive setting on Ref (§11.9): flag-style setting marking a relationship as visually inactive. Visualization tools render dotted lines and may exclude inactive relationships from cardinality computation. Useful for documenting historical or deprecated FKs.

  • color setting on Ref (§11.9): formal documentation of the color setting (#rgb or #rrggbb) on relationship lines. Previously informal.

TableGroup visualization ​

  • color setting on TableGroup (§16.2): formal documentation of the color setting on TableGroup, controlling the frame color in diagram renderers.

Records completeness ​

  • §25 Records expanded: top-level form records EntityName (col1, col2) { ... } documented alongside the inside-entity form. Cross-container reference form records core.users (...) { ... }. Full value-form table covering strings (single-line and triple-quoted multi-line), numbers, booleans, null, ISO 8601 dates, enum values (Status.active), and backtick expressions. Generator behavior matrix added (SQL INSERTs, MongoDB insertMany, Avro fixtures, JSON documents, markdown tables).

Examples ​

  • Multi-file module-system example (examples/09-modules-conformed-dimensions.xdbml + examples/10-modules-consumer.xdbml): demonstrates the canonical conformed-dimensions pattern that motivated the module system design, as a pair of first-class examples (each with its own page) cross-linking via their descriptions. The consumer file imports three dimension entities into its Container sales via Container-scoped reuse directives with clone blocks, and imports four scalar Named Types at file scope. Showcases Container-scoped imports (entities become sales.dim_customer not core.dim_customer), multiple imports per directive with one shared clone block, cloned_at metadata, transitive reuse semantics, and scalar Named Type usage. The example also exercises new minor additions: entity-level checks { } blocks (on dim_customer, dim_product, dim_date, fact_sales), color: on TableGroups, and inline check: field settings.

  • Example manifest extended with optional companionFiles array supporting multi-file examples where one file is the "headline" and one or more peers are not worth their own viewing page. scripts/prepare-examples.mjs copies companion files to public/examples/ alongside the primary; the viewing page lists companions in a dedicated block. Not currently used by any bundled example (the v0.2 module-system pair are first-class peer examples) but available for future asymmetric file-pair scenarios.

  • Example regeneration script gained orphan cleanup -- scripts/prepare-examples.mjs now detects and removes .xdbml files in public/examples/ and viewing pages in examples/ (matching the NN-slug.md shape) that no longer correspond to a manifest entry. This catches stale files left behind by renames, removals, or restructuring. Hand-authored files like README.md and index.md are never touched.

Changed ​

  • Strict-superset claim sharpened: every valid DBML document parses correctly under xDBML rules, and every DBML construct (used in a way valid in DBML) means the same thing in xDBML as in DBML. When a file declares xdbml: 0.2, it has opted into xDBML's extended semantics; the meaning of constructs in that context may legitimately differ from DBML's. The version directive selects which semantics apply.

  • Section renumbering: insertion of new §10 Checks between §9 Index and §10 Relationship shifted §10-§29 from v0.1 to §11-§30 in v0.2. All 38 cross-references updated throughout the spec, the grammar test cases, and the supporting documentation. The previous renumbering (from v0.2 draft 1, when §25 Module system was inserted) is now subsumed in this final shape.

  • Conformance §30: now requires v0.2 implementations to also parse v0.1 documents with v0.1 semantics, and to implement the module system per §26.6.

Not changed (compatibility) ​

  • v0.1 documents continue to parse correctly under v0.2 parsers with v0.1 semantics.
  • DBML 3.13.6 documents (no version directive) continue to parse with DBML semantics.
  • All v0.1 constructs preserve their behavior.

Deferred to later phase ​

  • URL imports in use/reuse directives (e.g., from 'https://...'). Phase 1 of v0.2 supports relative paths only.

v0.1 -- 2026 ​

Status: Superseded by v0.2 (still supported) Released: 2026

Added ​

Initial public draft of the xDBML specification. Strict superset of DBML 3.13.6 adding:

  • Explicit namespace-level declarations (Container)
  • Nested hierarchical structures (objects, arrays of records)
  • Polymorphism via oneOf/anyOf/allOf with discriminators
  • First-class JSON column type with known shape
  • Polyglot vocabulary (Entity/Collection/Record synonyms; engine-native type systems)
  • Named reusable types (object-shaped)
  • AI-readiness metadata (synonyms, business_term, tags, granularity)
  • Precise relationship cardinality (UML-style min..max strings)
  • Property-bearing edges for graph models
  • Views with source-query metadata
  • Validation constraints (pattern, minLength, maxLength, range, check)
  • Sample data via Records
  • DiagramView for visual layout metadata
  • TablePartial for entity-level field composition
  • Custom properties via x_ prefix convention

Twenty-eight chapters plus five appendices covering lexical conventions, language constructs, AST representation, conformance, and DBML 3.13.6 compatibility.


See SPEC INDEX for the latest specification and version history. See GOVERNANCE.md §9 for the stability commitments that govern version transitions.

Spec under Apache License 2.0 · Examples under CC0 1.0