agentsclimarketplace

Pbip project structure

Skill wardawgmalvicious/claude-config/skills/pbip-project-structure

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 pbip-project-structure

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

Use when working with PBIP (Power BI Project) folders — the text-based developer format that replaces binary .pbix. Covers folder layout (.SemanticModel/, .Report/, definition/, StaticResources/), entry-point files (.pbip, .pbir, .pbism, .platform), byPath vs byConnection (thick vs thin report), .pbism version 4.2 / TMDL signalling, PBIX-to-PBIP extraction (OPC ZIP, UTF-16LE vs UTF-8 internals), forking a project with new logicalId GUIDs, the rename cascade across TMDL/PBIR/DAX/bookmark/reportExtensions locations, and git hygiene (UTF-8 no BOM, CRLF, 260-char path limit, gitignoring diagramLayout.json). Invoke when user mentions .pbip, .pbir, .pbism, .platform, logicalId, PBIP conversion, forking a report, or setting up a Power BI repo for source control.

SKILL.md

10.7 KB, as published. Nobody here has run it

PBIP Project Structure

Power BI Project (PBIP) is the text-based developer format for Power BI. Replaces the .pbix binary with a folder of UTF-8 text files (TMDL for semantic models, PBIR JSON for reports).

Folder Layout

<ProjectName>/
+-- <Name>.pbip                              # Optional entry point
+-- .gitignore                               # Auto-generated
+-- <Name>.SemanticModel/
|   +-- .pbi/
|   |   +-- localSettings.json               # Gitignored
|   |   +-- cache.abf                        # Data cache, gitignored
|   |   +-- unappliedChanges.json            # Pending PQ changes
|   +-- definition.pbism                     # SM entry point
|   +-- definition/                          # TMDL files
|   +-- model.bim                            # TMSL legacy (mutually exclusive)
|   +-- diagramLayout.json
|   +-- DAXQueries/                          # .dax query view tabs
|   +-- TMDLScripts/                         # .tmdl script view tabs
|   +-- .platform                            # Fabric identity
+-- <Name>.Report/
    +-- .pbi/localSettings.json              # Gitignored
    +-- definition.pbir                      # Report entry point
    +-- definition/                          # PBIR JSON files
    +-- report.json                          # PBIR-Legacy (legacy alt)
    +-- mobileState.json                     # No external edit
    +-- semanticModelDiagramLayout.json      # Diagram node positions
    +-- CustomVisuals/                       # Private .pbiviz metadata
    +-- StaticResources/RegisteredResources/ # Themes, images
    +-- DAXQueries/                          # Report-level .dax files
    +-- .platform

Entry-Point Files

FilePurpose
.pbipProject entry point. Optional — definition.pbir can be opened directly
.pbirReport entry point. Points at semantic model via byPath or byConnection
.pbismSemantic model entry point. version: "4.2" for TMDL
.platformFabric identity per item: displayName, type, logicalId
version.jsonInside definition/ — PBIR schema version ("2.0.0")

.pbip

{
  "version": "1.0",
  "artifacts": [{ "report": { "path": "MyReport.Report" } }],
  "settings": { "enableAutoRecovery": true }
}

.platform

{
  "$schema": "https://developer.microsoft.com/json-schemas/fabric/gitIntegration/platformProperties/2.0.0/schema.json",
  "metadata": { "type": "Report", "displayName": "MyReport" },
  "config": { "version": "2.0", "logicalId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }
}
  • logicalId is the Fabric identity — never change on an existing deployed item
  • When forking a project, logicalId MUST be regenerated to a new GUID
  • type values: Report, SemanticModel

definition.pbir — byPath (Thick Project)

Report + model bundled in same project folder.

{
  "$schema": "https://developer.microsoft.com/json-schemas/fabric/item/report/definitionProperties/2.0.0/schema.json",
  "version": "4.0",
  "datasetReference": {
    "byPath": { "path": "../MyModel.SemanticModel" }
  }
}

definition.pbir — byConnection (Thin Report)

Report connects to a remote published semantic model. Preferred for managed/shared BI.

{
  "$schema": "https://developer.microsoft.com/json-schemas/fabric/item/report/definitionProperties/2.0.0/schema.json",
  "version": "4.0",
  "datasetReference": {
    "byConnection": {
      "connectionString": "Data Source=powerbi://api.powerbi.com/v1.0/myorg/WorkspaceName;Initial Catalog=ModelName"
    }
  }
}

For Fabric REST API deployments, use the id form: "connectionString": "semanticmodelid=[SemanticModelId]".

Do NOT use the legacy six-property form (pbiServiceModelId, pbiModelVirtualServerName, etc.) for new reports.

definition.pbism

{
  "$schema": "https://developer.microsoft.com/json-schemas/fabric/item/semanticModel/definitionProperties/1.0.0/schema.json",
  "version": "4.2",
  "settings": {}
}
versionSupported Format
1.0TMSL only (model.bim)
4.0+TMSL or TMDL (definition/ folder)

PBIX vs PBIP

AspectPBIXPBIP
FormatSingle binary ZIPFolder of text files
Source controlNot diff-friendlyGit-ready
External editingNot supportedVS Code, pbir CLI, scripts
Cached dataEmbeddedcache.abf gitignored
ConversionFile → Save As → PBIP in DesktopFile → Save As → PBIX in Desktop

PBIX Extraction

A .pbix is a ZIP (OPC) archive. Thick PBIX contains a DataModel binary (opaque ABF); thin PBIX has a Connections file instead. Modern PBIX stores reports in Report/definition/ (PBIR); legacy PBIX uses a monolithic Report/Layout (UTF-16LE).

PBIX Internal FileEncoding
Version, Settings, Metadata, Report/LayoutUTF-16LE
Connections, Report/definition/*UTF-8
[Content_Types].xmlUTF-8 with BOM
DataModel, SecurityBindingsBinary
import zipfile
with zipfile.ZipFile("MyReport.pbix") as z:
    z.extractall("MyReport_extracted")
# Detect type
is_thick = Path("MyReport_extracted/DataModel").exists()
is_modern = Path("MyReport_extracted/Report/definition/report.json").exists()

Assembling a PBIP from an extracted thin PBIX: copy Report/definition/ into <Name>.Report/definition/, generate definition.pbir with byConnection, generate .platform with a new logicalId GUID.

Thick PBIX cannot be converted programmatically — the DataModel binary is opaque. Use PBI Desktop File → Save As.

Forking a Project

  1. Copy the entire project folder, rename the root
  2. Rename .Report/ and .SemanticModel/ subfolders
  3. Update .pbipartifacts[].report.path
  4. Update .pbirdatasetReference.byPath.path (if byPath)
  5. Update each .platform (Report and SemanticModel separately) → set the appropriate displayName and regenerate a NEW unique logicalId per file (the two .platform files get different GUIDs)

Rename Cascade

Renaming a table, measure, or column requires updates in every location that references it. Missing one causes broken visuals or DAX errors.

LocationWhat to Update
TMDL table/measure/column declarationThe <type> Name line
TMDL partition nameMatches table name
model.tmdlref table Name + PBI_QueryOrder annotation
relationships.tmdlfromColumn: / toColumn: table prefix
All DAX expressionsBoth Table[ and 'Table'[ forms
visual.json query projectionsSourceRef.Entity, queryRef
visual.json filter/sort/CF blocksNested SourceRef.Entity, From[].Entity
page.json filterConfigFrom[].Entity
Bookmark JSONsfilter.From[].Entity, expression.*.SourceRef.Entity, highlight.dataMap keys
SparklineData metadataCompact string SparklineData(Table.Measure_[...])
reportExtensions.jsonTop-level entities[].name AND nested references.measures[].entity
Culture .tmdl linguistic metadataConceptualEntity, ConceptualProperty
semanticModelDiagramLayout.jsonnodeIndex
DAX query filesBoth .SemanticModel/DAXQueries/ and .Report/DAXQueries/

Commonly missed: sortDefinition blocks, SparklineData compact strings, bookmark highlight.dataMap keys, the second DAX query location, references.measures[].entity nested inside reportExtensions.json.

Git-Friendly Organization

  • Set core.autocrlf or add * text=auto to .gitattributes — PBI Desktop writes CRLF
  • UTF-8 without BOM for all files — a BOM prefix causes parse errors
  • Keep the root path short — 260-char Windows path limit; page/visual GUID folders can exceed it
  • Rename PBIR GUID folders (0c32c81b...) to human names (Overview, lineChart_Sales) for better diffs. Folder names are freely renamable; page.json / visual.json filenames and internal name properties must NOT change
  • Multiple .Report/ and .SemanticModel/ folders can coexist in one project
  • Fabric Git Integration only processes definition.pbir — other .pbir files are ignored but can coexist

Gotchas

IssueCauseFix
Parse error on openFile saved with UTF-8 BOMRe-save as UTF-8 without BOM
260-char path error on Git cloneDeep GUID folder nestingShorten root path; rename GUID folders
PBI Desktop ignores external editsStale in-memory stateClose and reopen Desktop
Broken visual after table renameMissed cascade locationGrep for old name across .json, .tmdl, .dax (both DAXQueries folders)
Empty reportExtensions.json crashes Desktop"entities": [] is invalidDelete the file instead of leaving empty
Forked project shows as same Fabric itemlogicalId not regeneratedNew GUID per .platform on fork
Thick PBIX cannot be converted to PBIP via scriptDataModel binary is opaqueUse File → Save As in PBI Desktop
Merge conflicts in diagramLayout.jsonAuto-generated per userTreat as binary or add to .gitignore

Reference

See also

  • pbir-cli — pbir CLI command reference
  • pbir-report-workflow — report creation workflow
  • pbir-pages — page JSON structure
  • fabric-tmdl — TMDL authoring rules

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.