agentsclimarketplace

Fabric tmdl

Skill wardawgmalvicious/claude-config/skills/fabric-tmdl

Personal Claude Code config — skills, subagents, hooks, and rules for Microsoft Fabric and Power BI workflows on Windows. Cherry-pickable, no semver.

Install
npx -y skills add wardawgmalvicious/claude-config --skill fabric-tmdl

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

TMDL (Tabular Model Definition Language) authoring rules for Fabric and Power BI semantic models. Use when editing .tmdl files, adding measures or columns to a semantic model, defining relationships or calculation groups, working in a PBIP definition/ folder, configuring Direct Lake partitions, or debugging TMDL validation errors. Covers syntax (tabs not spaces, /// descriptions, single-quoting names), DAX measure patterns, row-level security roles, calendar groups, and common gotchas.

SKILL.md

9.0 KB, as published. Nobody here has run it

TMDL Authoring Rules

Syntax Rules (MUST follow)

  • TMDL uses tab indentation — every nesting level is exactly one tab (\t), NOT spaces. Spaces cause validation errors.
    • PowerShell: use `t
    • Bash: use $'\t' or literal tabs
  • Objects declared by type + name: table Customer, column ProductId, measure 'Total Sales'
  • Names with spaces or special chars (., =, :, ') must be in single quotes: column 'Order Date'
  • Descriptions use /// placed ABOVE the object — do NOT use the description property
  • // comments are NOT supported in TMDL
  • Do NOT add lineageTag on new objects — it is auto-generated
  • Multi-line DAX must be enclosed in triple backticks (```)
  • Place measures before columns in table definitions
  • formatString is required on every measure
  • Never set dataType on measures — it is inferred from DAX

Naming Conventions

  • Tables: business-friendly, no Fact/Dim prefixes. Plural for facts (Sales), singular for dimensions (Product)
  • Columns: readable with spaces (Order Date, Unit Price)
  • Measures: clear patterns (Total Sales, # Customers). Time intelligence: [measure], [measure (ly)], [measure (ytd)])

Column Rules

PropertyRule
dataTypeRequired. Use int64, decimal, string, dateTime, boolean. Avoid double
sourceColumnMust match partition source column name exactly
isHiddenSet for ID columns, foreign keys, system columns
summarizeBynone for non-aggregatable numerics (IDs, postal codes, year numbers)
isAvailableInMdxfalse for hidden columns not used in sort-by or hierarchies
sortByColumnFor text needing non-alphabetical sort (month names → month number)

Measure & DAX Rules

  • Always set formatString — Currency: $#,##0.00 | Percentage: 0.00% | Integer: #,##0 | Decimal: #,##0.00
  • Use DIVIDE() instead of / for safe division
  • Never use IFERROR — causes performance degradation
  • Prefix VAR names with _: VAR _totalSales = ...
  • Use displayFolder to organize measures into logical groups
  • Add /// descriptions to explain business logic

Relationship Rules

  • fromColumn: = many-side (fact); toColumn: = one-side (dimension)
  • Create relationships BEFORE measures that depend on them
  • Default: crossFilteringBehavior: oneDirection; add bothDirections only when needed
  • isActive: false for role-playing dimensions; use USERELATIONSHIP() in DAX
  • Both sides must have matching dataType
  • Set isKey: true on dimension primary key columns
  • Hide foreign keys on fact tables (isHidden: true)
  • No composite keys — use a single surrogate integer key

Calculation Groups

table 'Time Intelligence'
	calculationGroup
		calculationItem Current = SELECTEDMEASURE()
		calculationItem YTD = CALCULATE(SELECTEDMEASURE(), DATESYTD('Date'[Date]))
	column 'Time Intelligence'
		dataType: string
	partition 'Partition_Time Intelligence' = calculationGroup
  • calculationGroup keyword has NO name — just the keyword indented under the table
  • Partition type must be = calculationGroup (not = m or = calculated)
  • Use formatStringDefinition (not formatString) for calc items that override measure format

Security Roles

role RegionalManager
	modelPermission: read
	tablePermission Sales = [Region] = "East"
  • modelPermission: required — use read or readRefresh
  • Assign users via Power BI REST API, not TMDL: POST .../datasets/{id}/users with roles array
  • Do NOT use INFO.ROLES() / INFO.ROLEMEMBERSHIPS() via DAX — unreliable. Use the REST API.

Annotations

  • Do NOT add PBI_* annotations manually — they are Power BI internal metadata
  • Custom annotations are fine for documentation/tooling
  • Syntax: blank line before the first annotation; blank line between annotations; same indent as peer properties
column 'Product Name'
	dataType: string
	sourceColumn: Product Name

	annotation MyTool_Owner = analytics-team

model.tmdl Required Properties

model.tmdl is the root of the definition/ folder alongside database.tmdl, expressions.tmdl, functions.tmdl, relationships.tmdl, roles/, perspectives/, cultures/, and tables/.

model Model
	culture: en-US
	defaultPowerBIDataSourceVersion: powerBI_V3
	discourageImplicitMeasures
	sourceQueryCulture: en-US
	dataAccessOptions
		legacyRedirects
		returnErrorValuesAsNull

defaultPowerBIDataSourceVersion: powerBI_V3 is required for Import-mode models — without it, Import from JSON supported for V3 models only.


Direct Lake Configuration

  • ALL partitions must use EntityPartitionSource — no M/Power Query

  • A named expression pointing to the Lakehouse/Warehouse must be defined before tables:

    expression DL_Lakehouse =
    		let
    			Source = AzureStorage.DataLake("https://onelake.dfs.fabric.microsoft.com/<WorkspaceId>/<LakehouseId>", [HierarchicalNavigation=true])
    		in
    			Source
    
  • Each table partition references the expression:

    partition Sales = entity
    	mode: directLake
    	source
    		entityName: Sales
    		schemaName: dbo
    		expressionSource: DL_Lakehouse
    
  • dataType: binary columns are NOT supported in Direct Lake

  • Columns map directly via sourceColumn — no transforms


Gotchas

IssueCauseFix
InvalidLineType: Property! in database.tmdlBare compatibilityLevel: without database declarationStart the file with database <Name> on line 1
Import from JSON supported for V3 models onlyMissing defaultPowerBIDataSourceVersionAdd powerBI_V3 to model.tmdl
Spaces-for-tabs validation errorsEditor converted tabsForce literal tabs; configure editor not to expand
// comment ignored or invalidNot supportedUse /// on line above the object (descriptions only)
Measure has wrong inferred typedataType was set manuallyRemove dataType from measures — always inferred
Missing formatString errorsMeasure without formatStringAlways set per measure; use formatStringDefinition for dynamic
Calc item format ignoredUsed formatString instead of formatStringDefinitionformatStringDefinition is DAX-based; only it overrides the selected measure's format
Broken report binding after column renameStale lineageTag left in placeNever edit lineageTag; let Power BI regenerate only on creation
Role members ignoredAuthored member staticallyAssign via Power BI REST API (POST datasets/{id}/users)
INFO.ROLES() returns stale/missing dataKnown DAX surface unreliabilityQuery membership via REST API
Calendar name collisionName unique per-table but not per-modelCalendar names must be globally unique across the model
Direct Lake partition errorsbinary column in sourceCast away in upstream Lakehouse/Warehouse; drop the column
Perspective appears empty in Power BINo perspectiveTable childrenAdd at least one table + column/measure, or includeAll on a table
model.bim and definition/ both presentForgot to delete .bim after TMDL conversionRemove model.bim; they are mutually exclusive
TMDL conversion failsOld Microsoft.AnalysisServices.retail.amd64Upgrade NuGet package for TmdlSerializer
Hierarchy level references missing columnColumn removed or renamed without updating levellevel.column: must reference an existing same-table column
PBI_* annotation edits revertPower BI rewrites on saveDo not hand-author PBI internal annotations

Additional reference

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.