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,inactiveandundirectedare 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.undirectedon an Edge reads the same way, as §11.16.2 states. §11.10 says that theforeign_masterflag needs no value, rather than that it takes none. - Grammar (
grammar/xDBML.g4):flagSettingtakes the three flags,relationshipFlag, alone or with: trueor: false, andedgeSettingtakesundirectedalone.
Tooling
Relationship flags (§11.9, §11.16.2, §13.3): the tools read
undirected,inactiveandforeign_masterthe same way on every element that takes them. The name alone sets a flag, as do: trueand the quoted: 'true';: falseand: 'false'leave it unset, as absence does; any other value is an error. Before:- a Ref rejected
undirectedwritten alone (invalid-undirected), while an Edge accepted it, and an Edge accepted any value at all; - the diagram drew no arrows for
undirected: trueon a relationship between entities, and drew them forundirected: 'true', because the renderer read the setting as a string only; inactive: falsedrew the relationship as inactive, andforeign_master: falsemade 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: falsealso drew the version error of a v0.4 construct, and on a field without an inlineref:, theforeign-master-without-referror.
@xdbml/parseexportsisFlagSet(),isInactive()andRELATIONSHIP_FLAGS, andisUndirected()takes an Edge as well as a Ref. The resolver reportsinvalid-undirectedon an Edge as on a Ref, andinvalid-inactiveandinvalid-foreign-masterfor 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/parseaccepts documents declaringxdbml: 0.7.5.- a Ref rejected
Grammar corpus: four v0.7.5 cases.
Not changed (compatibility)
- The examples of the spec keep
undirected: true, and tools keep writingforeign_masterandinactivealone. - Two forms that parsed without an error now draw one:
undirectedwith a value other than true or false on an Edge, andinactiveorforeign_masterwith such a value, which read as set.inactive: falseandforeign_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 onscalarTypestates 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 ZONEincluded, when the declaration stands on a line of its own, and marks theScalarTypewithunquotedWords. The resolver reportsunquoted-type-name, a warning, for each one in the document's own declarations.@xdbml/parseaccepts documents declaringxdbml: 0.7.4.- Renderer and playground: show the type as written. The help page of the diagnostics panel describes the warning.
llms.txtand 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 fieldaof typeint 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,mapandunion, a type of several words that follows no name still takes the quotes:array [double precision]keeps naming an elementdoubleof typeprecision(§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
derivationis renamedcalculation, in the list of kinds, the examples, the checks (§27.10.5), the drawing (§27.10.6), the OpenLineage table, where it still maps toDIRECT/TRANSFORMATION(§27.10.7), the validation summary (§27.12) and Appendix A. - Grammar (
grammar/xDBML.g4): the comments namecalculation, and state that a dependency between two entities takesidentityonly.
Tooling
@xdbml/parse: readscalculationas a kind of transformation, and reportsderivationwithunknown-transformation.@xdbml/parseaccepts documents declaringxdbml: 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.txtand the MCP server: teachcalculation.- Example 19 and the grammar corpus use
calculation; the corpus gains a v0.7.3 case wherederivationis an error.
Not changed (compatibility)
- A document of v0.7.2 that writes
transformation: derivationgets anunknown-transformationerror. 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_byandconditional(§27.10.1), with a table of where each appears in a SQL statement. A line withouttransformationcarries no kind.identitybetween 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 keepsidentity, and aCASTwritten in the statement makes aderivation. §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_queryholds the SQL, and aDepwithtransformation: aggregationstates 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, withdialectas an alias, on aDepand on a View (§14.5);transformationon 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.zorb.z <- a.x, a.ystates 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), andcollectLineage()gives one pair per upstream end, the pairs of one line sharing itslineand settings. Each pair carries itstransformation, and eachLineageitssourceQueryandsourceDatabase;dialectreads assource_database, on a View too. New diagnostics:unknown-transformation,transformation-between-entitiesandtransformation-target-mismatch(errors),transformation-mismatchandindirect-from-entity(warnings), andsource-query-in-settingsfor aDep. New exports:TRANSFORMATIONS,targetCoverage()andgrainOf(). Accepts documents declaringxdbml: 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;identityshows 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.indirectLineagehides 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,dialectandgrainas setting keys, and Monarch colors the eight kinds as values. - VS Code extension 0.7.2: the highlighting above.
llms.txtand 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, aMappingblock, several downstream ends, a kind between two entities), and a checklist item.- Grammar:
depSpectakes adepEndListon each side of the arrow.grammar/test-cases.mdgains 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_customersmoves from its note intosource_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 oneDepper 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".
EntityPartialandEntityGroupare the keywords;TablePartialandTableGroup, 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):
EntitiesandEntityGroupslist whatTablesandTableGroupslist; 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 useEntities. - 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; aMetadata DiagramViewblock does the same (§24.7.3). Until now a diagram view took thex_prefix only, andunknown-diagram-view-settingreported any other key. The rows of that condition leave the validation summaries of §18.6 and §24.7.6. - Metadata kinds (§24.7.1):
EntityGrouptargets an EntityGroup, asTableGroupdoes, in a document declaringxdbml: 0.7or later. - Module system (§28.3): the element types
entitypartialandentitygroupimport an EntityPartial and an EntityGroup, astablepartialandtablegroupdo. - Header color (§7.5, §14.5, §17.1):
colorsets the header color of an entity, a View and an EntityPartial in a document that declares a version, withheadercolor, 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 aMetadata TableorMetadata Viewblock,colorsets the header color too, and a block that writes both sets it twice, an error (§24.7.3). Without a version declaration,coloron aTablestays a custom property, as DBML 10.3.1 reads it, and onlyheadercolorsets 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 asx_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):tablePartialDefinitionandtableGroupDefinitiontakeEntityPartialandEntityGroup,diagramViewCategoryKeywordtakesEntitiesandEntityGroups, andmetadataKindtakesEntityGroup; the comments state thex-prefix and the reading ofcolor.
Tooling
@xdbml/parse: readsEntityPartialandEntityGroupasTablePartialDeclarationandTableGroupDeclarationnodes, which gain akeywordfield holding the word as written; readsEntitiesandEntityGroupsas the categoriesTablesandTableGroups, with the keyword as written inkeyword, as forSchemas; reads the Metadata kindEntityGroup, and the import element typesentitypartialandentitygroup. In a document that declares a version, acolorsetting on an entity, a View or an EntityPartial, or in aMetadata TableorMetadata Viewblock, gets thenameheadercolorand keepscolorinnameSource, asrequiredreads asnot null, so every consumer, the renderer among them, reads one key. A diagram view and aMetadata DiagramViewblock take any custom property; the diagnostic codeunknown-diagram-view-settingis no longer reported and stays in theDiagnosticCodetype. Diagnostics name the keyword as written (EntityGroup member 'x' does not resolve), the duplicate-setting messages name both words whencolorandheadercolormeet, and the messages of diagram views point toEntitiesandEntityGroupsin a document that declares a version. New export:isCustomPropertyName(), true for anx_or anx-key.@xdbml/parseaccepts documents declaringxdbml: 0.7.1.- Playground: the field inspector lists
x-keys under Custom properties withx_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 andcolor. - Highlighting: Monarch and the TextMate grammar color
EntityPartial,EntityGroup, the categoriesEntitiesandEntityGroups,Metadata EntityGroup, and a quoted"x-..."key before:as a custom property. - VS Code extension 0.7.1: the highlighting above.
llms.txtand the MCP server: writeEntityPartial,EntityGroup,Entities,EntityGroupsandcoloron an entity; custom properties may start withx_, or withx-in double quotes.- Examples: 09, 10 and 11 declare their groups with
EntityGroup, and 16 lists its entities underEntities. - Grammar corpus: a v0.7.1 section on the new keywords, the categories,
colorandheadercolor, andx-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
TablesandTableGroupsstay as they were. - One reading changes: in a document that declares a version,
coloron 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 TableorMetadata Viewblock of a document that declares a version and writes bothcolorandheadercolorsets the header color twice, an error, wherecolorwas 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.recordsstarts a block before{,(or[(§3.10), so a field may still be namedrecords. - 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 afterrecordsin 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.6or 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 withsource:andtarget: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 kindsTable,Column,TableGroupandNoteare read in every document;Entity,Collection,Record,Field,Containerand its synonyms,ViewandDiagramViewneedxdbml: 0.7or 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 (headercoloron a Table or a View,coloron a TableGroup, a sticky note or a Container), and custom properties; a diagram view takesnoteandx_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
metadataelement type. - Validation summary (§24.7.6) for Metadata blocks.
- Sticky notes (§19.2): a named top-level
Noteis 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 formNote core.legendplaces the note in the implicit Containercore. The colon form after a name, which DBML rejects, reads the same. - Settings of a sticky note (§19.3):
color(#rgb,#rrggbb, ornonefor the text alone) and custom properties, as DBML 8.3 and 9.1 added them, read in every document; acolorof another form is an error. AMetadata Noteblock sets the same settings. - Sticky notes in a Container (§19.4): with
xdbml: 0.7, a namedNotein 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 unnamedNoteof a Container body stays the Container's note. - The note of the project (§19.5): a top-level
Notewithout a name is the note of the project, as aNotein 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-levelNote; 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
Deprecords 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 -> borb <- 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 askey: valuelines or aNote, 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 nodep. - Settings (§27.3):
note, rendered as Markdown, so a query fits in a fenced code block;color,#rgbor#rrggbb; and custom properties. - Rules (§27.4): a
Depstands 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_querystate 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'sresolveFileSpecifierdoes; otherwise it is read relative to the importing file, with or without./, soauth,sub/authand@shared-usersare valid, and an absolute path is an error unless the host resolves it. Without an extension,<path>.xdbmlis read, or<path>.dbmlwhen 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
nullare case-insensitive, and the readings of DBML apply by column type: a quoted number for a numeric column,'Y'/'N','T'/'F',1/0for 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 lacksnot nulland 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):recordsBlockandtopLevelRecordsare defined in the grammar rather than inherited from DBML, with the column list, the settings brackets and empty fields. - Grammar (
grammar/xDBML.g4): anOPTIONAL_OPERATORtoken, 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, exceptcheckandref, which repeat by design. DBML rejects a repeated setting. - §6.4, §14.5:
coloron a Container andheadercoloron 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
NotesasTablesnarrows 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_onimplies 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 nodepelement type; the AST gains aDepnode. Conformance gains item 24. The FAQ gains a question on data lineage. - Grammar (
grammar/xDBML.g4):depDefinition,depSpec,depBodyItemanddepSetting, and the arrowsARROW_RIGHT(->) andARROW_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/parseaccepts documents declaringxdbml: 0.7. A records block inside an entity carriescolumnswhen it has a column list; both records forms carrysettingsandexample; an empty field is aNullValuewithempty: 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:
recordstakes the block color before(and[as well as{, andexamplethe color of a setting flag. @xdbml/parse, relationships:RefSpecandRefValuecarrysourceOptionalandtargetOptionalbeside the operator, which keeps its four values.refCardinality()andrefCardinalityIn()return the cardinality of each end with its origin (setting,operator,optional-mark,nullable-foreign-key,unstated), andrefChildNullable()whether a child end may hold null. New diagnostic:cardinality-conflict.- Diagram: each end draws the cardinality
@xdbml/parsestates 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: aMetadataDeclarationnode (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 markedimported.metadataIndex()returns the settings blocks apply to each element,withMetadata()merges them with an element's own,metadataNote()andmetadataColor()read a note and a color, andresolveMetadataTarget()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:
Metadataand its kind color as declaration keywords, only together, so an entity or a field may still be namedmetadata. - 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: aNoteDeclarationcarriessettings, 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 holdsNoteDeclarationbeside its ownNoteBlock.stickyNotes()lists the sticky notes with their ids (sales.order_states),resolveStickyNote()resolves a name,projectNote()returns the note of the project, andisStickyNoteColor()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 withoutxdbml: 0.7drawsconstruct-requires-version, and two notes of one name in one scopeduplicate-declaration.@xdbml/parse, Metadata fixes: a color a kind takes (headercoloron a Table,coloron 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 inwithMetadata(); 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.
DiagramModelgainsnotes, and@xdbml/renderexportsparseMarkdown(),parseInline(),layoutNoteText()andsizeNote(). - 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; thestickyNotesvisibility 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 theNotesof 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:
Notecolors 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: aDepDeclarationnode (name,form,settings,bodySettings,headerSettings,dependencies), eachDepDependencywith itsupstreamanddownstreamendpoints, thearrowas written and its ownsettings; an inlinedepsetting holds aDepValue(arrow,target).collectLineage()lists every Dep, declarations first, then the inline settings, with an iddep:<n>that the diagram and the inspector share;lineageById(),depSettings(), which applies the precedence of the brackets and the body,depColor(),isDepColor()andcheckLineage()complete the API, withresolveLineage(), 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; aDepin 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,#f59e0bin the dark theme), or in thecolorof 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.DiagramModelgainsdeps, the theme gainslineage, and@xdbml/renderexportslayoutDeps()andlayoutDep(). - 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'shighlightedFieldsoption tints rows besides the selected field. Thelineagevisibility 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_nameis computed fromraw.first_nameandraw.last_name; the rows offct_ordersare computed from the rows ofstg_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:
Depcolors as a declaration keyword at the start of a line before:,{,[or a name, so a field may still be nameddep;depcolors 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.txtand the MCP server: a Data lineage section, aDeprow in the table of what goes where, two common mistakes (a relationship operator in aDep, an inlinedepon a field naming an entity), andDepamong the top-level declarations of the checklist.@xdbml/parse, modules:ParseOptions.resolveSpecifier(specifier, importerPath)maps a specifier to the key handed toreadFile, or returnsundefinedfor the default resolution of §28.13. An import without an extension asksreadFilefor<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.dbmlaslib.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 schemec:. A source whose first folder looks like a host is read as a folder, and the error when it holds no such file gives thehttps://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 thehttp:rejection of §28.14. llms.txtand the MCP server: documents declarexdbml: 0.7, and the checklist placesMetadatablocks at the top level. The MCP server's instructions give DBML 10.3.1 as the baseline andxdbml: 0.7as the first line; its reference test reads a part without a version line asxdbml: 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
Metadatablocks, 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 andmy.tableis the entitytableof the Containermy. A note gives the state of@xdbml/parsebefore 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): anameSegmentrule, 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~nameline, and the name of a Project or aRef.
Tooling
- Quoted names in every name position:
@xdbml/parsereads a quoted identifier at the head of a path, so in the key lines of aconstraintsblock, inindexes, and at both ends of aRef, 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-levelrecordsdeclaration and its columns; after~; in an import path and its alias; in a tuple element or a polymorphic alternative; and as the name of aRef,Ref "fk order customer": .... It used to reject these forms, at the head of a path withExpected path start identifier. - A dot inside quotes stays in the name: the parser read
"my.table"andmy.tableas one name, so a quoted name holding a dot reached the entitytableof the Containermy, 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 Orderfor"sales"."Work Order","my.table"for"my.table". A name without such characters reads exactly as before.@xdbml/parseexportsjoinName(),splitName(),quoteNameSegment(),nameQualifier(),lastNameSegment()andisQualifiedName()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 fielduser.idstay two fields, in a key as in the diagram. @xdbml/parseaccepts documents declaringxdbml: 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/parserejected, now parses as it does in DBML 3.13.6.~"name"is the one form@xdbml/parsereads 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
~nameline. A~nameline 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~nameline. This is the behavior of upstream DBML. A~nameline may also sit among the fields of anobject { }or ajson { }, and in the body of a Type or of an entry of adefinitionsblock, 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'sconstraintsblock 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 itsindexesblock, 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~nameline 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
~nameline 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~nameline. The section used to list the entity's own fields first, then the injected ones. - Indexes (§9): one sentence on the
indexesblock 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
~nameline 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/parseexportseffectiveFields(), which returns the fields of an Entity or an Edge with the injected fields in place, each with the TablePartial it comes from, andeffectiveFieldList()for the fields of an object, a json body or a Type, next totablePartials()andinjectedPartials(). 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
Refendpoint that names an injected field resolves, at the top level and along a nested path. It used to reportunresolved-field, as did a column of a top-levelrecordsdeclaration and a key path inconstraints; - 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
~nameline in the body of a Type or of an entry ofdefinitionsthat names no TablePartial reportsunresolved-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/parseexportseffectiveIndexes(),effectiveHeaderColor()andeffectiveNote(). 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~nameline in the body of a TablePartial or inside one of its fields, whatever version a document declares. The playground help lists it.@xdbml/parseaccepts documents declaringxdbml: 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 onerenderToSVGdraws 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, asclaim_submissiondid 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/parseread without a diagnostic now draws an error: a~nameline 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 theTypekeyword, 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$defsordefinitionsin 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:
definitionsamong the block keywords.
Tooling
Parser (
@xdbml/parse): readsdefinitions { }in an entity body, only before{, as aDefinitionsBlockwhose entries areTypeDeclarationnodes in the object or the scalar form; the entries form a list body. Adefinitionsblock 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, inRefpaths and composite endpoints, and in the key paths ofconstraints. A name written in the body of a project Type, or in a field received from a TablePartial, resolves at project scope.resolveNames()reportsdefinitions-outside-entity,duplicate-definitions-block,duplicate-definition,named-type-shadows-builtin(for an entry) anddefinitions-unsupported-targetas errors, anddefinition-shadows-typeas 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 theLocalTypestype. The target helpers move fromconstraints.tsto a newtargets.ts;constraints.tsre-exportsisRelationalTarget, so existing imports keep working. The editor colorsdefinitionsbefore{. Readsxdbml: 0.6.5.Grammar:
definitionsBlock,definitionEntryandDEFINITIONS, withdefinitionsamong the field names.grammar/test-cases.mdgains thirteen v0.6.5 cases, each checked against the parser, two of them importing from the new fixturegrammar/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 importsentityDefinitionsfrom@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
definitionsblocks, one of them recursive and two used asoneOfalternatives; a MongoDB collection declares an Address of another shape under the same name; an Oracle table reuses the Project-level TypeMonetaryAmount, since relational targets take no internal definitions.VS Code extension 0.6.5:
definitionsis colored as a block keyword before{, asindexes,checks,constraintsandrecordsare; a field nameddefinitionskeeps the color of a field name. The TextMate grammar takesdefinitionsfromBLOCK_KEYWORDSinparser/src/keywords.ts, and its test checks both readings. The extension skips 0.6.4, which changed no highlighting.llms.txtand 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:$defsinstead ofdefinitions, 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.tsis regenerated from it, and every new example is checked against the parser.
Changed
Spec
Body keywords (§3.10):
definitionsis the seventh word that starts an element, only when{follows it, so a field may still be nameddefinitions. 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/parseread nodefinitionsblock 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 { }andreuse { }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 { ... }orType int stringis 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 diagnosticsinvalid-tuple-positionsandnamed-type-shadows-builtin, with the severities above; the second compares names in any letter case, as built-in types match, soType Moneyis reported against the built-inmoney. Refusesuseandreusein a document declaringxdbml: 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./libfrom the working directory; module keys and import cycles use forward slashes, which Node's fs accepts on Windows. Readsxdbml: 0.6.4.Grammar test corpus: all 131 cases of
grammar/test-cases.mdnow run in the parser's test suite, sonpm run checkfails when a case and the parser disagree. ThreeRefcases and the polyglot case declare the entities they name; the module cases import from small files ingrammar/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 declarexdbml: 0.6, each beside a case declaring v0.5 that expects the warning; the case of a patch release newer than the parser declaresxdbml: 0.6.99, a version no release reaches.Example 06 (financial services): its
Type MoneybecomesType MonetaryAmount. Its fourMoneyfields had the built-inmoneytype 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 underViews, Containers underContainers, 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, unlessTablesnames some of its entities orViewssome 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 aRefdo: 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 eachRefand 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): readsDiagramView, where it raised "Unknown top-level construct", in every document, as aDiagramViewDeclarationwith one category node per category,Schemasread asContainers, and a flag for the body-level{ * }. AnEdgescategory, an unknown category, and aDiagramViewin a Container fail the parse with a message that cites §18.1.diagramViews()lists the diagram views of a document after module resolution, anddiagramViewMembers()returns the members of one (§18.2) with the Containers, TableGroups and supertype groups drawn around them (§18.4).resolveNames()reportsunresolved-diagram-view-name,ambiguous-diagram-view-name,diagram-view-wrong-category,duplicate-diagram-viewandduplicate-diagram-view-category,unknown-diagram-view-settingfor a setting other thannoteor a custom property, andconstruct-requires-versionforContainers,ViewsorSupertypeGroups, 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 viewX, where it imported a database view of that name; the import is refused in a Container body, and areuse *in a Container body moves an imported diagram view to the top level. The editor colors the category keywords before{. Readsxdbml: 0.6.3.Grammar:
diagramViewDefinition, which the grammar referenced without defining, withdiagramViewCategory,diagramViewBodyItemandDIAGRAM_VIEW; a diagram view takes a settings block and notes.grammar/test-cases.mdgains fifteen v0.6.3 cases, each checked against the parser.Renderer (
@xdbml/render):buildDiagram(doc, collapsedPaths, { diagramView })and thediagramViewoption ofrenderToSVGdraw 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 keepsfkordkwhen the other end is outside the diagram view, and every id stays the id it has in the full diagram. The interactive mount takes adiagramViewoption and gainsgetDiagramView()andsetDiagramView(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_viewquery parameter,diagramViewin 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/healthlists the option.MCP server and llms.txt:
render_xdbmltakesdiagram_view, refuses an undeclared name with the list of declared ones, and names the diagram view in its summary.validate_xdbmlcounts and names the diagram views, in its summary and its JSON tail. llms.txt gains a Diagram views section, aDiagramViewrow in What goes where (with its note and custom properties), a Common mistake for anEdgescategory, andDiagramViewamong the top-level declarations of the checklist;mcp/src/reference.tsis 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 scopekeyword.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
Edgescategory removed (§18.1): v0.1 through v0.6.2 listed anEdgescategory. ARefor an Edge appears in a diagram view when both of its ends do, soEdgesis now an error.Schemascategory (§18.1): accepted as an alias ofContainers, 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.1heading, dropped in 0.6.2, is restored.
Not changed (compatibility)
- Backward-compatible throughout.
@xdbml/parseread noDiagramViewbefore 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
Refand of an inlineref:. An alias that repeats the name of an entity or a Container names nothing. - Target array types (§8.1):
text[]andvarchar(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#3498DBas a string where it raised a lex error, which rejected valid DBML. ResolvesRef: V.id ...and[ref: > V.id]through the alias ofTable very_long as V, where it reportedunresolved-entity; the key rule of §11.17 applies through the alias. Keeps[]intext[], where it dropped the brackets and read the field astext. Reportscontainer-target-missing,container-target-not-in-projectandname-collision; before, a Type and an Entity with one name replaced each other in the symbol table. Readsxdbml: 0.6.2. - Grammar:
HEX_COLORas a setting value, and( LBRACK RBRACK )*after a scalar type.grammar/test-cases.mdgains six v0.6.2 cases. - Renderer (
@xdbml/render) and playground: a type such asvarchar(20)[]displays as written rather than asvarchar[](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
Refin a Container, relationship settings beside an inlineref:, 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.tsis 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, ajson { }schema, aoneOf/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.indexesstays out: there,email, namemay 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,recordsandsource_querystart an element only when:or{, as the element requires, follows them. Before v0.6.1 the parser rejected a field namednote,indexes,checksorrecords, which DBML accepts, and a View field namedsource_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 inconstraintsor 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
notewritten in the body is an error;source_querywritten 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,recordsandsource_querystart their element only before:or{(§3.10), asconstraintsalready did. A setting written in a body, such asmaterialized: truein 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), andresolveNames()reportssource-query-in-settingsandduplicate-source-queryas warnings (§14.7), andduplicate-unique-keyas a warning (§10.3). When a primary key is declared twice on the same fields,duplicate-primary-keynames 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 namednotelike any other field name.Grammar:
listSeparatorin every list body; replacement rules fortableDefinition,tablePartialDefinition,enumDefinitionandtableGroupDefinition;fieldName, which accepts the six body keywords; and a View whosesource_queryis a body element only.grammar/test-cases.mdgains 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 namednoteandrecordsdraw rows, and that a View's source query draws none.Playground: the inspector shows a View's source query through
viewSourceQuery(), the firstsource_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 andduplicate-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 onesource_query:element of the body.mcp/src/reference.tsis regenerated; the server serves it once redeployed with 0.6.1. The MCP tests cover commas, a column namednote,materializedin a View body and a bracketedsource_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,unionand array rows keep their rules; therecordsrow, 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
materializedin 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. Asource_queryin 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, withoutsource_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,checksorrecords, 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 namedsource_queryfailed the same way.Parser:
xdbml: 0.6.1failed with "Unexpected token Dot", though §4 and the grammar allowMAJOR.MINOR[.PATCH]. The parser reads the patch number and supports documents up to 0.6.1:xdbml: 0.6stays valid, andxdbml: 0.6.2is refused as newer than supported.Editor grammars: the TextMate grammar colored
note varcharas aNotedeclaration, with the type colored as the declared name.Noteis 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/parse0.6.1 and@xdbml/render0.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_queryin 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 namednote,indexes,checksorrecords.
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 withpkorunique; a quoted line declares a check.constraintsstarts a block only when{follows, so a field may keep that name.Keys (§10.2):
nameandnotesettings, 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 inconstraints.[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. Apkentry inindexesreads as the primary key, with itsnameas 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 nullin the AST whether or not the document says so, andnullwritten 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, inchecks { }and in the field-levelcheck: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,
nullon 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
uniqueindex 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
Refis 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): readsxdbml: 0.6, theconstraints { }block (new AST nodesConstraintsBlockandKeyConstraintEntry) and quoted check expressions (CheckEntry.delimiterrecords the form).entityConstraints(),primaryKey()andbodyConstraints()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 impliednot 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-- andref-target-not-keyfor §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,keyConstraintand a quotedcheckEntryingrammar/xDBML.g4;grammar/test-cases.mdgains ten v0.6 cases, each checked against the parser.Editor grammars:
constraintsis 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 colorsindexes,checks,constraintsandrecordswhere 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): apkline inconstraintsgives each field of the key the PK badge, like apkentry inindexes, and auniqueline 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 compositeuniqueentry inindexesdeclares an index and stays unbadged. Primary key fields of a 0.6 document show the not-null marker, since the parser marks themnot 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 inconstraints(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.tsis 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,uniqueandcheck:rows point to §10; the §9 example declares its primary key inconstraints; 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.6and move their composite primary keys from[pk]on several fields toconstraints(§10.3). Example 08 names its keys and adds a capacity check; example 10 moves its line-total check intoconstraintsand writes its field-level checks in single quotes.05-healthcare-fhir: the
patientsindexes namedfamily_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_TYPESinparser/src/keywords.tslists 72 common type names of specific targets: Oraclenumber,clobandraw, PostgreSQLserial,byteaandcitext, SQL Serveruniqueidentifieranddatetimeoffset, Snowflaketimestamp_ntz, BigQueryfloat64, the integer types of Cassandra, Protobuf and ClickHouse, andgeometry,geographyandinterval. 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 inSCALAR_TYPESis 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 thepossible-type-typowarning.mcp/src/reference.tsis 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-typefor every scalar type name missing from the parser's highlighting lists, among them Oraclenumber,clobandraw, PostgreSQLserialandbytea, SQL Serveruniqueidentifier, and thenumber(10)of the §27.16 field-level import example. A field typed by a declaredEnum, 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 newpossible-type-typowarning, with the declared name as the suggestion.unresolved-typestays inDiagnosticCode, 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 reportsinvalid-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 aduplicate-declaration. A qualified type name that names no Enum, such aspublic.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 atypeNamerule underscalarType, andgrammar/test-cases.mdgains a valid and an invalid case.
Packages
@xdbml/parse0.5.1 and@xdbml/render0.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-typefor 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-typeremains inDiagnosticCode.
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
SupertypeGroupdeclaration, 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 emitsundefinedGroup1,undefinedGroup2, and so on.Completeness and exclusivity (§12.3):
completeness: total | partialandexclusivity: disjoint | overlapping, set per group, so one supertype can carry a total, disjoint axis and a partial, overlapping one. Absence means unstated, as withconstraint_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 | nestedfor roll-up, anddiscriminator, which reuses the keyword of §21.2 and is rejected on an overlapping group. A subtype member may carry its ownstrategy, 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
SupertypeGroupscategory.Module system (§27.3):
supertypegroupis an importable element type.AST (§28): a
SupertypeGroupnode withSubtypechildren. 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.5line 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 inspec/vN.M.mdopens 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 asnpm run check:spec-links, innpm test, and in CI. Arguments that are not.mdfiles are ignored with a warning, sincenpm testpasses its own extra arguments to this last script in its chain.Parser:
SupertypeGroupparses to aSupertypeGroupDeclarationwithSupertypeGroupMemberchildren, each member carrying its own settings. Members are separated like TableGroup members. A group without a name is a parse error.supertypegroupis a selective-import element type and survives aliasing, clone blocks andflatten(). The keyword joinsDECLARATION_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 MCPvalidate_xdbmltool 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 warningsempty-supertype-groupandmerge-without-roll-up. Duplicate group names reuseduplicate-declaration; a group in a document declaring less than 0.5 reusesconstruct-requires-version.@xdbml/parseexportssupertypeGroupSettings()andsubtypeStrategy()(canonical values),canonicalSupertypeGroupValue()andSUPERTYPE_GROUP_VALUES(the alias table),resolveSupertypeGroups()(members resolved to entity ids),supertypeChains()(nearest supertype first), andcheckSupertypeGroups(). None of them computes the attributes or keys of a physical model.Grammar:
supertypeGroupDefinitionand its settings and member rules ingrammar/xDBML.g4, with aSEMICOLONtoken; ten cases ingrammar/test-cases.md, each verified against the parser.Renderer: supertype groups draw per spec §12.9.
DiagramModel.supertypeGroupscarries each group resolved against the canvas, with members resolved by the parser'sresolveSupertypeGroups()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/renderexportslayoutSupertypeGroups(),supertypeGroupAnchorX(),SYMBOL_RADIUS,SYMBOL_STEMand theSupertypeGroupLayoutandSupertypeGroupGeometrytypes.Relationship names: a
showRelationshipNamesrender option, and arelationshipNamesvisibility key in the interactive mount, draw the name of a namedRefat 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.RelationshipVisibilitygainssupertypeGroups(default shown) andrelationshipNames(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.5and 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.tsis regenerated frompublic/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
SupertypeGroupand the setting keyssupertype,completeness,exclusivity,strategyandmerge. Regenerating the extension's copy also brings inforeign_masterandconstraint_typefrom v0.4, which had not been copied into it. No new.vsixis 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 → xDBMLdirection 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.4is parsed by 0.4+ parsers;xdbml: 0.5is 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 aParseErrorwhose new optionalcodeisunsupported-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.mjsinrenderer/andplayground/) resolves@xdbml/parsetoparser/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/parsenormally.Parser tests parse the repository's examples: the suite now reads
examples/rather than private copies underparser/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/currentfollows fromscripts/prepare-spec.mjswith 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
Refmay carry aforeign_masterflag, which records where a duplicated attribute of denormalized data is mastered. ARefwithout the flag is referential -- the foreign key xDBML has always expressed. The flag is available in the short, long, and inline declaration forms.foreign_masterbeside an inlineref:(§11.10.2): the one exception to the rule that settings are not supported on inlineref:declarations. A field holds at most one inlineref:, 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, anddm. An implementation computes all four from the set ofRefdeclarations; 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_rolename the role the entity at each end plays, andsource_verb/target_verbcarry 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_identifyingrecords 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 fromnon_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
Refendpoint 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 nofk/fm/dk/dmmarkers, and takes its reading direction from the operator alone, with-meaning linked without a stated direction and<>rejected.undirected: truemarks a relationship that reads the same way from both ends, matchingundirectedon an Edge.Settings block on the long
Refform (§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_verbandundirectedare recognized settings.constraint_typetakesidentifyingornon_identifyingand is rejected on a foreign master;undirectedtakes true or false;<>is rejected between entities; the documentation settings are gated onxdbml: 0.4like theforeign_masterflag. New diagnostics:invalid-constraint-type,constraint-type-on-foreign-master,invalid-undirected,entity-level-many-to-many,ambiguous-ref-endpoint.@xdbml/parseexportsconstraintType,isUndirected,entityNamesandisEntityLevelEndpoint.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/renderexportscollectRefDeclarations, one definition of the order the diagram numbers relationships in.RefLayoutcarriesrelationshipType,inactive,entityLevel,undirectedanddecl.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 (
unitPriceis not a foreign master, because the price charged may differ from the catalogue price).MCP server and llms.txt: both teach
xdbml: 0.4and the new constructs. CI now fails whenmcp/src/reference.tsdrifts frompublic/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 throughnpm versionandnpm pkg setsopackage.jsonandpackage-lock.jsonmove 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 aHEADthat differs fromorigin/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_VERSIONnow readspackage.jsoninstead of restating the version insrc/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
RefDeclarationstatements alone, so a line from an inline ref opened an empty pane. Both now index intocollectRefDeclarations.
Changed
Spec
inactiveline 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
Refa 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 forarray [union [T1, T2, ...]], andlistandsetbehave the same. The listed members must be the types aunionaccepts (scalar or named types, ornull) and must be comma-separated -- the comma is what distinguishes the shorthand from the named-element formarray [name type]. This lets a schema describe the heterogeneous arrays JSON and BSON allow without writingunion. 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/oneOfbodies, comma-and/or-newline for tuples, comma-required formapandunion, and the element-type shorthand forarray/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/setholds a single element type expression (which may itself be polymorphic), that JSON/BSON arrays need not be uniform, and points tounion(scalar mix),oneOf(object-shape mix, §20.5), a tuple (fixed positions, §8.6), or an opaquejsonfield 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.mdand in the spec examples that used inline comma-separated fields (which the formal.g4does 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_xdbmlmode parameter: the render tool acceptsmode: "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/parse0.3.2 (tuple fix, array/set shorthand) and@xdbml/render0.3.1 (dark-mode theming) were published; the MCP server was redeployed on@xdbml/render0.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
uniontype, and requires no change to a document'sxdbml:version.
v0.3 -- 2026
Status: Draft -- superseded by v0.3.1 Released: 2026
Added
Module system
Remote module sources (§26.14): a
useorreusedirective may now import a module from anhttps://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-symbolasaliasing, and directive settings behave identically whether the string afterfromis 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
httpsscheme is permitted. A source usinghttp,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/renderpackage: 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/parseand@xdbml/renderpackaged for external consumption: both build todistwith declaration output and anexportsmap, 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):
useandreusedirectives 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/reusedirective 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_atmetadata 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/reusedirectives 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 Containerorderingis namedordering.products, notcore.products.from,use,reuse,asas 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 optionalname:andnote:settings. The block is a peer of theindexes { }block. Required for full DBML 3.13.6 compatibility -- DBML documents withchecks { }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
inactivesetting 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.colorsetting on Ref (§11.9): formal documentation of thecolorsetting (#rgbor#rrggbb) on relationship lines. Previously informal.
TableGroup visualization
colorsetting on TableGroup (§16.2): formal documentation of thecolorsetting 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 formrecords 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 itsContainer salesvia Container-scopedreusedirectives with clone blocks, and imports four scalar Named Types at file scope. Showcases Container-scoped imports (entities becomesales.dim_customernotcore.dim_customer), multiple imports per directive with one shared clone block,cloned_atmetadata, transitivereusesemantics, and scalar Named Type usage. The example also exercises new minor additions: entity-levelchecks { }blocks (ondim_customer,dim_product,dim_date,fact_sales),color:on TableGroups, and inlinecheck:field settings.Example manifest extended with optional
companionFilesarray 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.mjscopies companion files topublic/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.mjsnow detects and removes.xdbmlfiles inpublic/examples/and viewing pages inexamples/(matching theNN-slug.mdshape) that no longer correspond to a manifest entry. This catches stale files left behind by renames, removals, or restructuring. Hand-authored files likeREADME.mdandindex.mdare 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/reusedirectives (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/allOfwith 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.