Build a Power BI Report with Claude Code

Author a multi-page Power BI PBIP report as reviewable code using Claude Code.

Introduction

30 Second Summary

Business intelligence teams increasingly need reports that are version-controlled, peer-reviewable, and reproducible. Traditional .pbix files are opaque binaries that make collaboration difficult.

In this project, you will use Claude Code with the skills-for-fabric plugin to build a complete Power BI report as reviewable code, following the PBIP format that stores semantic models and reports as plain-text files in a Git repository.

What You'll Build

A multi-page "VanArsdel Sales Performance" Power BI report authored entirely by an AI agent in PBIP format (TMDL semantic model + PBIR report), version-controlled in a professional Git repository.

By the end of this project, you'll have:

  • A validated, multi-page Power BI report authored entirely as diff-able PBIP code by Claude Code.
  • A professional Git repository following Datapot's monorepo conventions with a dataset contract, data dictionary, model design docs, and build log.
  • A session recap document showing the full process: prompts used, what went well, what broke, lessons learned, and cost.
  • Secret Mission: Add a fourth report page with advanced DAX time intelligence and conditional formatting on a variance matrix.

Are there any prerequisites?

You need Power BI Desktop installed with PBIR preview features enabled, Claude Code access, Git installed, and a GitHub account. The first step walks you through confirming your setup.

Set Up the Repository

Every Power BI project starts with a decision: do you save as a binary .pbix file, or do you write the report as reviewable code? The PBIP format splits your report into two text-based layers: TMDL for the semantic model and PBIR for the report layout.

In this step, you will use Claude Code to scaffold a monorepo that follows Datapot's conventions, copy in the VanArsdel dataset, and make your first Git commit. This gives you a version-controlled foundation before any modeling or report authoring begins.

In this step, get ready to:
  • Scaffold the monorepo folder structure using Claude Code.
  • Copy VanArsdel raw data files into the repository.
  • Initialize Git and make the first commit.
Scaffold the monorepo with Claude Code

Datapot's datapot-fabric-agent-demo repository uses a specific folder layout: each report lives under reports/ with subfolders for the dataset, dictionary, docs, and the PBIP project itself. You will replicate this structure so your submission follows the same conventions.

  • Switch back to Claude Code.
  • Paste the following prompt to scaffold the monorepo:
Create a multi-report Power BI PBIP monorepo following these conventions:
- Root: README.md, CLAUDE.md, CONVENTIONS.md, LICENSE (MIT)
- shared/themes/ — a brand theme JSON
- reports/01-vanarsdel-sales-performance/ with subfolders:
  dataset/, dictionary/, docs/, pbip/
- docs/ at root level with session-recap.md template
- Use the datapot-fabric-agent-demo structure as reference.
- Include strict TMDL rules in CLAUDE.md:
  * No // comments at TMDL root
  * /// descriptions immediately above declaration (no blank line after)
  * No blank line after block headers
  * LF line endings, UTF-8, no BOM
  * Page/visual folder names: word characters or hyphens ONLY (no .Page/.Visual suffix)
  * report.json themes MUST include reportVersionAtImport

What does this prompt produce?

Claude Code creates the entire folder tree and populates each file with appropriate content:

  • CLAUDE.md encodes the strict TMDL/PBIR rules that Power BI Desktop enforces but command-line validators miss.
  • CONVENTIONS.md documents the monorepo layout so future contributors (or your future self) understand the structure.
  • docs/session-recap.md provides a template for your build retrospective with sections for prompts used, debug log, and lessons learned.
  • shared/themes/brand-theme.json gives you a centralized theme file that the report will reference.

Why encode TMDL rules in CLAUDE.md?

Power BI Desktop has a strict parser that rejects files other validators accept. For example, a // comment at the TMDL root level passes tmdl-validate but causes Desktop to fail silently.

By putting these rules in CLAUDE.md, Claude Code reads them at the start of every session and avoids the expensive debug spiral that cost the demo repo most of its time.

  • Wait for Claude Code to finish creating all files.
  • Confirm the folder structure exists by running:
find . -type f | head -20

You should see paths like ./CLAUDE.md, ./CONVENTIONS.md, ./LICENSE, ./README.md, ./docs/session-recap.md, ./shared/themes/brand-theme.json, and the reports/01-vanarsdel-sales-performance/ subfolders.

Missing files or folders?

  • If Claude Code only created some files, paste a follow-up prompt: "Please also create the missing files from the list above." It will fill in the gaps.
  • If Claude Code asked a clarifying question instead of creating files, answer it and then re-submit the scaffold prompt.
  • Check that you are in the correct working directory. Claude Code creates files relative to where it was launched.

Still stuck? Ask for help: why didn't Claude Code create my full monorepo scaffold?

Copy VanArsdel data files into the repository

The VanArsdel dataset comes from the email attachment Datapot sent you. These Excel files contain the sales, product, geography, and budget data your report will analyze.

  • Download the VanArsdel Excel file(s) from the email attachment to a temporary location on your machine.
  • Copy them into the raw data folder by running:
cp ~/Downloads/*.xlsx reports/01-vanarsdel-sales-performance/dataset/raw/

Adjust the source path

If you saved the email attachments somewhere other than ~/Downloads/, replace that path with the actual location. The destination folder reports/01-vanarsdel-sales-performance/dataset/raw/ must stay the same.

  • Confirm the files landed in the right place by running:
ls reports/01-vanarsdel-sales-performance/dataset/raw/

You should see one or more .xlsx files listed.

No files showing up?

  • Double-check the source path in the cp command matches where your browser actually saved the downloads.
  • Make sure the dataset/raw/ folder exists. If Claude Code missed it, create it manually with mkdir -p reports/01-vanarsdel-sales-performance/dataset/raw/.

Need more help? Ask: I can't find or copy my VanArsdel Excel files into the repository.

Initialize Git and make the first commit

With all files in place, you can initialize the repository and lock in this clean starting state. Every change from here forward will be tracked as a diff.

  • Initialize a Git repository and make your first commit by running these commands:
git init
git add .
git commit -m "chore: scaffold monorepo with TMDL rules and VanArsdel raw data"

What do these commands do?

  • git init creates a new Git repository in the current folder.
  • git add . stages every file in the scaffold for the commit.
  • git commit -m "..." saves a snapshot of the current state with a descriptive message.

You should see output listing the files committed. The exact count depends on how many files Claude Code generated and how many Excel files you copied in.

Git commit failed?

  • If you see a message about identity, Git needs your name and email configured. Run git config user.name "Your Name" and git config user.email "you@example.com", then retry the commit.
  • If you see "nothing to commit", the files may already be committed. Run git log --oneline to check.

Still having trouble? Ask: my git commit is failing or showing unexpected behavior.

✔️ Awesome, I've got everything!

Great. Double check that you have saved all files and that git status shows a clean working tree (nothing to commit).

ⓧ I'd like to double check the full structure

Run the following to see your complete folder tree:

find . -not -path './.git/*' | sort

You should see a structure like this:

.
./CLAUDE.md
./CONVENTIONS.md
./LICENSE
./README.md
./docs
./docs/session-recap.md
./reports
./reports/01-vanarsdel-sales-performance
./reports/01-vanarsdel-sales-performance/dataset
./reports/01-vanarsdel-sales-performance/dataset/raw
./reports/01-vanarsdel-sales-performance/dataset/raw/<your-vanarsdel-file>.xlsx
./reports/01-vanarsdel-sales-performance/dictionary
./reports/01-vanarsdel-sales-performance/docs
./reports/01-vanarsdel-sales-performance/pbip
./shared
./shared/themes
./shared/themes/brand-theme.json

Your exact file names in dataset/raw/ will vary depending on what Datapot sent. The key is that the folder structure matches and CLAUDE.md contains the strict TMDL/PBIR rules.

Your repository is scaffolded, your raw data is in place, and Git is tracking everything. Next up, you will profile the dataset and write a formal contract so Claude Code knows exactly what columns and tables it has to work with.

Profile the Dataset and Write the Contract

Your repository scaffold is in place and the VanArsdel data files are sitting in dataset/raw/. Now comes the most important phase of the entire project: understanding your data before touching a single model file.

The Datapot demo repository's biggest time and cost sink was rebuilding the semantic model after real data arrived. Their own session recap states: "The domain-assumption schema did not survive contact with the real export." By profiling your dataset first, you avoid that expensive mistake entirely.

In this step, get ready to:
  • Profile every file, sheet, and column in the VanArsdel dataset.
  • Write a dataset contract and star schema model design based on actual data.
  • Define a 3-page report specification for Sales Performance.
Profile the dataset with Claude Code

Data profiling means systematically examining every file, table, and column to understand what you actually have. This step produces the foundation every other document in your project builds on.

  • In Claude Code, give it this prompt to profile the raw dataset files:
Profile the dataset files in dataset/raw/:
- List every file, every sheet/table, row count, columns with types
- Distinct values, null rates, date ranges
- Identify candidate facts vs dimensions
- Check referential integrity of keys
Write the profile to docs/data-profile.md

What does this prompt do?

Claude Code opens each Excel file in dataset/raw/ and examines the structure. It records row counts, column names and types, null rates, and distinct value counts for every field.

It also identifies which tables look like facts (numeric measures at a transactional grain) versus dimensions (descriptive attributes with surrogate keys). The referential integrity check confirms that foreign keys in fact tables match primary keys in dimension tables.

  • Wait for Claude Code to finish generating the profile.
  • Confirm that docs/data-profile.md now exists by running:
ls docs/data-profile.md
  • Open data-profile.md and scan through it. You should see every file listed with its sheets, row counts, column names, types, and null rates.

Why profile before modeling?

Datapot's own demo repository spent most of its debug time rebuilding a model that was based on assumptions rather than real data. Their lesson: "The domain-assumption schema did not survive contact with the real export. The first model was a proposal, not the answer."

By profiling first, you ensure the star schema you design in the next substep matches what actually exists in the data. No guessing, no expensive rework.

Claude Code not reading the Excel files?

Make sure the Excel files are actually in reports/01-vanarsdel-sales-performance/dataset/raw/ and not a subfolder inside it. Claude Code needs direct access to the .xlsx files.

If Claude Code reports a dependency error reading Excel, it may need to install a Python library like openpyxl. Allow it to install the package when prompted.

Still stuck? Claude Code can't read my Excel files in dataset/raw. What should I check?

Write the dataset contract and model design

With the profile in hand, you can now define two critical documents. The dataset contract locks down what columns exist and how they're named. The model design proposes how those columns become a star schema.

  • Give Claude Code this prompt to generate both documents based on the profile:
Based on the data profile:
1. Write dataset/DATASET-CONTRACT.md listing every expected file and its columns (snake_case)
2. Write docs/model-design.md proposing a star schema:
   - Identify fact tables (Sales Actuals, Budget/Forecast)
   - Identify dimensions (Date, Product, Manufacturer, Geography)
   - Define grain, relationships, and planned measures
   - Address Actuals vs Budget comparison (shared dimensions, separate facts)
3. Confirm the design with me before building the model.

What do these documents do?

  • DATASET-CONTRACT.md acts as a contract between the raw data and the semantic model. It lists every source file and its expected columns in snake_case format. If the data changes, this document shows exactly what broke.
  • model-design.md proposes how the raw tables become a star schema with fact tables (Sales Actuals, Budget/Forecast) filtered by shared dimensions (Date, Product, Manufacturer, Geography). This is your blueprint for the TMDL semantic model you'll build in the next step.
  • Wait for Claude Code to present its proposed star schema design.
  • Review the proposed model carefully. Verify that every table and column referenced in model-design.md actually exists in your data-profile.md.
  • Check that relationships make sense. Each dimension's primary key should appear as a foreign key in the fact table(s).
  • If anything looks wrong, tell Claude Code what needs adjusting before confirming.

The VanArsdel typical schema

Most VanArsdel datasets include a Sales fact table (revenue, units, ProductID, Date, GeoID, ManufacturerID) with dimension tables for Product, Manufacturer, Geography, and Date. Some versions include separate Budget or Forecast tables. Your exact columns depend on which version Datapot sent you.

  • Confirm the generated files exist by running:
ls reports/01-vanarsdel-sales-performance/dataset/DATASET-CONTRACT.md
ls reports/01-vanarsdel-sales-performance/docs/model-design.md
  • You should see both files listed without errors.

Model design references columns that don't exist in your data?

This is the exact failure the profile-first approach prevents. If Claude Code's proposed model includes columns not in your profile, tell it explicitly: "Column X does not exist in my data. Check data-profile.md and revise."

Claude Code sometimes invents columns based on common VanArsdel schemas it has seen in training data. Always cross-reference against YOUR profile, not generic expectations.

Need help? Claude Code proposed columns that don't match my actual data profile. How do I get it to use only the columns in my dataset?

Write the report specification

The report spec defines what the final Power BI report will look like. It tells Claude Code exactly which pages, visuals, and measures to build when you move to the PBIR phase.

  • Give Claude Code this prompt to create the report specification:
Write a report spec for a 3-page "Sales Performance" report:
- Page 1: Sales Overview — KPI cards (Total Revenue, Units Sold, Avg Price, Market Share),
  monthly revenue trend, revenue by product category, geographic breakdown
- Page 2: Budget vs Actuals — variance KPIs, monthly actuals vs budget trend,
  variance by product/region, a matrix with conditional formatting
- Page 3: Competitive Analysis — market share trend, VanArsdel vs competitors by segment,
  top/bottom products by growth

Audience: Sales leadership / CMO
Page size: 1280x720
Format: measures only (no implicit aggregation), theme-based formatting

What does this spec control?

  • Page size 1280x720 matches Datapot's report conventions for consistent layout across the monorepo.
  • "Measures only" means every visual binds to a DAX measure. No raw column aggregations. This follows the discourageImplicitMeasures setting you'll enable in the semantic model.
  • "Theme-based formatting" means visual styles come from the shared brand theme, not per-visual overrides. This keeps the report maintainable.
  • Confirm the report spec was created by running:
ls reports/01-vanarsdel-sales-performance/docs/report-spec.md
  • Open report-spec.md and verify it includes all three pages with their visual layouts, the audience, page size, and formatting rules.

Report spec references measures that don't match your model design?

The report spec should only reference measures that are feasible given your model design. If a KPI like "Market Share" appears in the spec but your dataset has no competitor data, ask Claude Code to adjust: "My data doesn't support Market Share calculations. Replace with a measure my data can support."

Having trouble aligning the spec? Help me reconcile my report spec with my actual dataset capabilities.

At this point, commit your documentation progress by running:

git add .
git commit -m "docs: data profile, dataset contract, model design, and report spec"

Why commit documentation before building?

Committing now creates a checkpoint you can return to if the model build goes wrong. It also shows Datapot your deliberate, phased process. Their demo repository commits at each lifecycle phase too.

Your documentation foundation is locked in. Every file has been profiled, the contract defines what columns exist, the model design proposes how they relate, and the report spec describes what you'll build. Next, you'll turn that model design into actual TMDL code that Power BI Desktop can open.

Build the Semantic Model

Your documentation is locked in. The data profile, model design, and dataset contract give Claude Code everything it needs to generate a precise TMDL semantic model without guessing at column names or relationships.

The semantic model is the analytical backbone of your Power BI report. Every visual, measure, and filter depends on it being correctly structured. In this step, you'll prompt Claude Code to generate the full model, validate it, and document every table and measure in a data dictionary.

In this step, get ready to:
  • Generate the full TMDL semantic model following strict formatting rules.
  • Validate every .tmdl file against Power BI Desktop's parser requirements.
  • Write a complete data dictionary covering all tables and measures.
Generate the TMDL semantic model

A star schema keeps your model fast and predictable. Dimensions filter facts through single-direction relationships, and all analysis happens through explicit measures.

Claude Code will read your model-design.md and DATASET-CONTRACT.md to generate the model. The prompt below encodes the strict rules that Power BI Desktop enforces but that linters miss.

  • Give Claude Code this prompt to build the full semantic model:
Build the TMDL semantic model based on model-design.md and DATASET-CONTRACT.md.
Follow these STRICT rules:
- Star schema: dimensions filter facts (single-direction relationships)
- Surrogate keys (*_key columns): isHidden + isKey on dimensions
- Fact columns: all hidden; analysis only through measures
- All measures on a 'Key Measures' disconnected table, grouped by displayFolder
  (01 Volume, 02 Revenue, 03 Budget & Variance, 04 Market, 05 Time Intelligence)
- Date table: calculated CALENDAR covering the data range, dataCategory: Time
- Import mode with DataFolder parameter for file paths
- discourageImplicitMeasures: true
- NO // comments at TMDL root level
- NO blank line after /// descriptions
- NO blank line after block headers
- LF line endings, UTF-8, no BOM
- Quote names with spaces/parens/% : 'Revenue (USD)', 'Market Share %'

What does this prompt do?

Each rule in this prompt addresses a specific Power BI Desktop parser requirement. The Desktop parser is stricter than any command-line validator.

  • Root-level // comments cause a TMDL format error when Desktop opens the file.
  • A blank line after a /// description triggers an invalid line type error because Desktop expects the declaration immediately after.
  • The DataFolder parameter lets table partitions reference data files without hardcoded paths.
  • Grouping measures by displayFolder keeps the field list organized for report authors.
  • Wait for Claude Code to generate the .tmdl files. They should appear inside reports/01-vanarsdel-sales-performance/pbip/VanArsdelSalesPerformance.SemanticModel/.
  • Review the output. Check that dimension tables, fact tables, and the Key Measures table are all present.

Profile-first saves rebuilds

The Datapot demo repo's biggest cost was rebuilding the model after real data arrived. Because you profiled first and confirmed the design, Claude Code generates a model that matches your actual columns from the start.

Claude Code generated unexpected column names?

If the generated TMDL references columns that don't match your dataset, Claude Code may have hallucinated names from generic VanArsdel knowledge. Paste the relevant section of DATASET-CONTRACT.md back into the conversation and ask it to reconcile.

Help me fix mismatched column names in my TMDL

Validate TMDL before proceeding

Command-line validators catch structural issues, but Power BI Desktop's parser is stricter. You need both automated validation AND a manual check for the rules that linters miss.

  • Give Claude Code this validation prompt:
Validate the TMDL:
1. Run tmdl-validate on every .tmdl file
2. Manually check: no root-level // comments, no orphaned ///, no blank after block headers
3. Verify every relationship has matching keys in both tables
4. Verify every measure references only existing columns/measures
5. Verify the DataFolder parameter is used in all partition queries
Report any issues.

Why both automated and manual checks?

The bundled tmdl-validate linter is lightweight. It catches syntax errors but misses several rules that Power BI Desktop rejects at open time.

The Datapot demo learned this the hard way. Three tools reported success while Desktop still refused to open the file. Testing in Desktop early saves hours of debugging later.

  • If Claude Code reports any issues, let it fix them and re-validate until all checks pass.
  • Confirm you see zero errors across all validation checks before moving on.

Validation keeps failing on relationship keys?

This usually means a surrogate key column name in a dimension table doesn't match the foreign key column name in the fact table. Open model-design.md and compare the relationship definitions against the actual .tmdl files.

Help me debug mismatched relationship keys

Write the data dictionary

A data dictionary documents every table, column, and measure so that anyone reviewing your repository understands the model without opening Power BI Desktop.

  • Prompt Claude Code to generate the dictionary:
Generate dictionary/data-dictionary.md and dictionary/data-dictionary.csv:
- Per table: grain, source file, and every column (Model Name | Source Column | Type | Description | Example)
- Separate section for all measures (Measure | Display Folder | Format | DAX | Definition)
- Keep in lock-step with the TMDL files

Why both Markdown and CSV?

The Markdown version is human-readable in GitHub. The CSV version is machine-parseable for automated checks that verify the dictionary stays in sync with the TMDL files.

  • Verify the generated dictionary by spot-checking 3-4 measures against the actual .tmdl files. The DAX formulas should match exactly.
  • Confirm both dictionary/data-dictionary.md and dictionary/data-dictionary.csv exist inside reports/01-vanarsdel-sales-performance/.
  • Commit your progress by running:
git add .
git commit -m "feat: TMDL semantic model with validation and data dictionary"

Dictionary doesn't match the TMDL?

If Claude Code generated the dictionary from model-design.md instead of the actual .tmdl files, the names may be out of sync. Explicitly tell Claude Code to read the final TMDL files (not the design doc) when generating the dictionary.

Help me regenerate my data dictionary from the TMDL files

Your semantic model is validated and documented. Next up, you'll build the PBIR report that brings these measures to life across three pages.

Build the Report

Your semantic model is validated and ready to serve data. Now comes the visual layer: the PBIR report that gives leadership a way to explore sales performance across products, regions, and time.

PBIR stores every page and visual as individual JSON files in a folder hierarchy. This makes reports diff-able and reviewable, but it also means folder names and JSON structure must follow strict rules that Power BI Desktop enforces silently.

In this step, get ready to:
  • Generate a 3-page PBIR report using Claude Code following strict naming and wiring conventions.
  • Validate all PBIR JSON and cross-check visual bindings against your semantic model.
  • Open and refresh the report in Power BI Desktop to confirm all pages render with data.
Generate the 3-page PBIR report

Your report-spec.md defines what each page should show. Now you will hand that spec to Claude Code along with strict PBIR conventions so it generates a report that Power BI Desktop will actually accept.

  • Give Claude Code the following prompt to build the full 3-page report:
Build the PBIR report based on report-spec.md. CRITICAL rules:
- Page/visual folder names: word characters or hyphens ONLY
  CORRECT: Overview, BudgetVariance, Card_Revenue
  WRONG: Overview.Page, Card_Revenue.Visual
- report.json: MUST include reportVersionAtImport on BOTH baseTheme and customTheme:
  { "visual": "5.10.0", "report": "5.10.0", "page": "5.10.0" }
- pages.json pageOrder entry == page folder name == page.json name
- activePageName must be in pageOrder
- All visuals bind to measures (never raw columns)
- Page size 1280x720
- Visuals start at y >= 120 (title area above)
- Register the shared theme from shared/themes/
- Use KPI cards -> trend -> breakdown -> detail layout per page

What do these rules enforce?

Each rule prevents a specific silent failure in Power BI Desktop:

  • Folder names with .Page or .Visual suffixes cause Desktop to silently ignore those pages (you get a "no pages" error).
  • Missing reportVersionAtImport causes Desktop to reject report.json entirely, cascading into the same "no pages" failure.
  • Mismatched pages.json entries versus folder names means Desktop cannot find the page definitions.
  • Binding to raw columns instead of measures breaks when discourageImplicitMeasures is enabled on the semantic model.
  • Wait for Claude Code to finish generating the report files.
  • Confirm the output folder reports/01-vanarsdel-sales-performance/pbip/VanArsdelSalesPerformance.Report/ exists with page subfolders.
  • Verify the page folders by running:
ls reports/01-vanarsdel-sales-performance/pbip/VanArsdelSalesPerformance.Report/

You should see page folders using only word characters or hyphens (e.g. Overview, BudgetVariance, CompetitiveAnalysis) plus report.json and pages.json.

Seeing .Page or .Visual suffixes in folder names?

Claude Code sometimes generates the older PBIR naming convention. Paste this correction prompt:

"Rename all page and visual folders to remove .Page and .Visual suffixes. Use word characters or hyphens only. Update pages.json and page.json name fields to match."

help me fix PBIR page folder naming for Power BI Desktop

Run full project validation

Before opening in Desktop, catch structural errors programmatically. This saves you from the slow Desktop debug cycle.

  • Give Claude Code this validation prompt:
Validate the complete PBIP project:
1. python validate_pbip.py reports/01-vanarsdel-sales-performance/pbip --no-pbir-cli
2. jq empty on every .json file in the Report folder
3. Cross-check: every visual's dataField references exist in the semantic model
4. Verify pages.json wiring matches folder structure
Target: 0 errors on all checks.

What do these validation steps check?

  • validate_pbip.py checks TMDL syntax, relationship integrity, and PBIR structural rules.
  • jq empty confirms every JSON file is valid (no trailing commas, no unescaped characters).
  • The cross-check ensures no visual references a measure or column that does not exist in your TMDL model.
  • pages.json wiring verification confirms every pageOrder entry points to a real folder with a matching page.json name.
  • Review Claude Code's output. You should see 0 errors across all four checks.

Validation reporting errors?

If errors appear, paste the exact error output back to Claude Code with the prompt: "Fix these validation errors" followed by the full output. Common causes include mismatched measure names between visuals and the TMDL, or JSON syntax errors from malformed strings.

Do not proceed to Power BI Desktop until all four checks pass.

help me debug PBIP validation errors

Test in Power BI Desktop

Command-line validators catch structural problems, but Power BI Desktop is the ultimate judge. Three tools can report success while Desktop still refuses to open the file.

  • Switch to Power BI Desktop.
  • Click File in the top menu bar.
  • Click Open report.
  • Navigate to reports/01-vanarsdel-sales-performance/pbip/ and select VanArsdelSalesPerformance.pbip.
  • Click Open.

What should I see?

Power BI Desktop should open the project and display your report pages in the page tabs at the bottom. You should see all 3 pages listed: Sales Overview, Budget vs Actuals, and Competitive Analysis.

The visuals will be empty until you set the DataFolder parameter and refresh.

Seeing errors when Desktop opens the file?

This is normal during PBIR development. Desktop's parser is stricter than any command-line validator. Copy the exact error message (including Document, Line Number, and Line Type if shown) and paste it back to Claude Code with this prompt:

New bug, fix please:
[[ERROR_MESSAGE="paste exact error including Document, Line Number, Line Type"]]

Keep iterating until Desktop opens cleanly with all 3 pages visible in the bottom tabs.

Once Desktop opens without errors, set the data source parameter so your visuals populate with actual data.

  • Click the Home tab in the ribbon.
  • Click Transform data, then select Edit parameters.
  • Set the DataFolder parameter to the full path of your dataset/raw/ folder (e.g. C:\Users\you\Desktop\repo\reports\01-vanarsdel-sales-performance\dataset\raw\).
  • Click OK.
  • Click Apply changes in the yellow notification bar that appears at the top of the report.

Power BI Desktop will refresh all tables. After the refresh completes, your visuals should populate with VanArsdel sales data across all 3 pages.

Visuals still blank after refresh?

Check that your DataFolder path points to the folder containing the actual Excel files, not a parent folder. The path must end with a backslash on Windows.

If you see "column not found" errors, a measure may reference a column name that differs from your actual data. Paste the error back to Claude Code to fix the TMDL partition query.

help me troubleshoot Power BI Desktop data refresh failures

Your report is live and rendering real VanArsdel data. Next up, you will write your session recap document, finalize all supporting docs, and push the complete repository to GitHub for submission.

Write the Session Recap and Ship

Your report opens cleanly in Power BI Desktop with all three pages rendering data correctly. The hardest technical work is done.

Now comes the piece that separates a good submission from a great one: the session recap. Datapot values transparency about your process just as much as the final result. Their own demo repo documented every failure openly. In this step, you will write an honest retrospective, finalize all documentation, and push the complete repository to GitHub.

In this step, get ready to:
  • Write a complete session recap documenting your prompts, successes, failures, and estimated cost.
  • Finalize all documentation and pass the project checklist.
  • Commit and push the repository to GitHub for submission.
Write the session recap document

The session recap is your differentiator. It shows Datapot that you understand agentic workflows well enough to reflect on them honestly.

Why write a session recap?

Datapot's own demo repo spent most of its debug time on issues that Desktop caught but CLI validators missed. They documented all of it transparently in their session recap.

Your recap proves self-awareness: you know what worked, what broke, and what you would do differently. That signal matters more than a polished result with no visible process.

  • Give Claude Code this prompt to write the full session recap in docs/session-recap.md:
Write docs/session-recap.md as a build retrospective. Structure:

## Summary
- What was built (1 report, X tables, Y measures, Z pages/visuals)
- Total time spent
- Estimated Claude Code cost (run /cost for actual)

## What Went Well
- [List specific things that worked smoothly — e.g. profile-first prevented model rework,
  strict TMDL rules avoided Desktop parse errors, etc.]

## What Did Not Go Well
- [List any issues/bugs encountered, how long they took to resolve, what the root cause was]
- [Be honest — if Desktop still rejected something, document it]

## Prompts Used
- [List every significant prompt given to Claude Code, in order, with a one-line note
  on what it achieved. Group by phase: Setup, Profile, Model, Report, Validate]

## Debug Log
- [Any errors encountered, exact error text, and what fixed them]

## Lessons Learned
- [What you would do differently next time]
- [What rules/patterns are worth reusing]

## Cost Breakdown
- Estimated input/output tokens
- Estimated cost
- Biggest cost driver (which phase)

What does this prompt produce?

  • The Summary section tallies what was built (tables, measures, pages, visuals) and gives a time/cost estimate.
  • What Went Well and What Did Not Go Well give Datapot honest signal about your self-awareness.
  • Prompts Used creates a chronological record grouped by phase (Setup, Profile, Model, Report, Validate).
  • The Debug Log captures exact error text and the fix for each issue.
  • Lessons Learned and Cost Breakdown round out the retrospective with actionable takeaways.
  • Review the generated recap and edit it for honesty.
  • If Claude Code glossed over a failure or made something sound smoother than it was, correct it.
  • Run /cost in Claude Code to get your actual token usage and cost, then update the Cost Breakdown section with those real numbers.

Be specific, not generic

A strong "What Went Well" entry is: "Profile-first approach meant the model matched real columns on the first attempt, avoiding the rebuild that cost the demo repo 45 minutes." A weak entry is: "Things went smoothly overall."

Finalize all documentation and run the checklist

With the recap written, the remaining documentation needs to be updated so that anyone exploring the repo can understand what was built and how to run it.

  • Give Claude Code this prompt to finalize all documentation files:
Finalize all documentation:
1. Update reports/01-vanarsdel-sales-performance/README.md with:
   - Domain, status, audience, model summary, how to open/refresh
2. Update docs/build-log.md with dated entries of what was done
3. Update root README.md with a link to session-recap.md and how to explore
4. Ensure data-dictionary matches final TMDL

What does this prompt produce?

  • reports/01-vanarsdel-sales-performance/README.md gets domain context, audience info, and instructions for opening the .pbip file and setting the DataFolder parameter.
  • docs/build-log.md records dated entries for every major decision and fix throughout the build.
  • The root README.md links to the session recap and tells reviewers how to explore the repo structure.
  • The data dictionary is cross-checked against the final TMDL to catch any drift.
  • Review the generated documentation to confirm it matches your actual project state.

Now run through the final checklist to confirm everything passes before you commit.

  • Confirm tmdl-validate passes on all .tmdl files.
  • Confirm validate_pbip.py reports 0 errors.
  • Confirm jq empty passes on every .json file in the Report folder.
  • Confirm the report opens in Power BI Desktop without errors.
  • Confirm all 3 pages display correctly with data after refresh.
  • Confirm the DataFolder parameter is documented in the report README.md.
  • Confirm no hardcoded absolute paths exist in any committed file.
  • Confirm session-recap.md is complete with prompts, lessons, and cost.
  • Confirm build-log.md records all decisions and fixes.

Checklist item failing?

If any validation command reports errors, paste the exact error output back into Claude Code with the prompt: "New bug, fix please:" followed by the error text. Claude Code will know the TMDL/PBIR rules from CLAUDE.md and can target the fix.

If the data dictionary drifted from the model, ask Claude Code to regenerate it from the current TMDL files.

Help me debug a failing checklist item in my PBIP project.

Commit and push to GitHub

With all checks passing, your repository is ready to ship. This final commit captures the complete project as your submission to Datapot.

  • Stage, commit, and push your project by running these commands:
git add .
git commit -m "feat: VanArsdel Sales Performance report - 3 pages, star schema, session recap included"
git remote add origin https://github.com/[[GH_USERNAME="YOUR-USERNAME"]]/[[GH_REPO="YOUR-REPO-NAME"]].git
git push -u origin main

What do these commands do?

  • git add . stages every file in the repository for commit.
  • git commit -m saves a snapshot with a descriptive message following conventional commit format.
  • git remote add origin links your local repo to the GitHub remote you created.
  • git push -u origin main uploads your code and sets the remote as the default tracking branch.
  • Open your repository on GitHub in your browser.
  • Verify the README.md renders correctly on the repository's main page.
  • Navigate to docs/session-recap.md and confirm it displays all sections: Summary, What Went Well, What Did Not Go Well, Prompts Used, Debug Log, Lessons Learned, and Cost Breakdown.
  • Check that the folder structure is visible and well-organized (the reports/, docs/, and shared/ directories should be clearly visible).

Push rejected or authentication failed?

Make sure you created an empty repository on GitHub first (no README, no .gitignore, no license). If the remote repo already has commits, the push will be rejected.

If you see an authentication error, confirm you have a GitHub personal access token or SSH key configured for your terminal.

Help me fix a git push error to GitHub.

This GitHub repository URL is what you will submit to Datapot. The repo contains everything a reviewer needs: the working report, the model, the documentation, and a transparent record of how you built it all with an AI agent.

Secret mission

Advanced Time Intelligence and Conditional Formatting

Your 3-page report covers the fundamentals, but sales leadership wants trend analysis that shows momentum over time. Add a fourth page with Year-over-Year growth, rolling averages, a revenue waterfall, and a conditional variance matrix that highlights where budget targets are being missed.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally. Claude Code usage is billed per your existing plan, but the resources you created (local files and a GitHub repository) have no ongoing costs.

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Local project folder on your Desktop (Git repository with PBIP files, documentation, and session recap).
  • GitHub repository (remote copy of your submission).

Keep everything running

No action needed. Choose this if you are still actively building or plan to reference the project for your Datapot submission.

  • Your local project folder stays on your Desktop.
  • Your GitHub repository remains accessible at its URL.
  • The PBIR preview features remain enabled in Power BI Desktop.
  • The skills-for-fabric plugin remains installed in Claude Code.

Pause - I'll come back to this later

No processes are running, so there is nothing to pause. Your files and GitHub repository persist without any ongoing cost.

  • If you want to free up Desktop space, move your local project folder to an external drive or cloud storage.
  • Your GitHub repository remains available whenever you return.

Delete - I don't want to use this again

Remove all project resources completely. Only do this after you have submitted your repository to Datapot and no longer need it.

  • Delete the local project folder by running the following command in your terminal.

macOS

cd ~/Desktop
rm -rf vanarsdel-sales-performance

Windows

cd ~\Desktop
rmdir /s /q vanarsdel-sales-performance

Use your actual folder name

Replace the folder name above with whatever you named your project directory. Check with ls (macOS) or dir (Windows) first to confirm the name.

  • Delete the GitHub repository.
  • Optionally, disable PBIR preview features in Power BI Desktop.

That's a Wrap!

That's a Wrap!

Nice work! You just built a complete, multi-page Power BI report authored entirely as reviewable PBIP code by an AI agent. Your submission-ready Git repository demonstrates the full agentic analytics engineering workflow, from data profiling through to a transparent session recap.

Here's what you learned:

  • Built a PBIP-format Power BI report (TMDL semantic model + PBIR report) using Claude Code with the skills-for-fabric plugin, following strict Desktop-enforced rules like word-character folder names and reportVersionAtImport requirements.
  • Applied a profile-first workflow that reconciles the actual dataset columns before modeling, avoiding the costly model-rebuild cycle that plagues assumption-driven approaches.
  • Shipped a submission-ready Git repository with professional documentation (dataset contract, data dictionary, model design, build log) and an honest session recap documenting prompts, successes, failures, and lessons learned.
  • Secret Mission: Added advanced DAX time intelligence measures (YoY growth, rolling averages, budget variance) and conditional formatting on a variance matrix for deeper analytical storytelling.

Ready to quiz yourself?