# Pbi Lifecycle

> End-to-end Power BI delivery project lifecycle — folder bootstrap, PBIP file scaffold, Git tracking with private GitHub repo, theme refresh propagation, and environment promotion (Dev → Test → Prod). One skill that takes a Power BI delivery project from "empty disk" to "deployable, version-controlled, env-promotable" without requiring the operator to know git, the PBIP file format, or the MAX_PATH 256 quirks of Power BI Desktop. Phase 1 (folder bootstrap, additive-only) is generic and runs against any client. Phase 2 (PBIP scaffold from scratch in PBIR-Legacy with Auto Date/Time disabled by default and theme auto-applied) generates a working .pbip file that opens cleanly. Phase 3 (Git + GitHub CLI integration with always-private repos and project-scoped commits) gives version control without the operator typing a single git command. Phases 4 and 5 (theme refresh, env promotion) ship as design-locked roadmap until real-PBIP calibration completes. Use this skill whenever the operator asks anything resembling st

- Skill: `gusbavia/pbi-lifecycle` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add gusbavia/pbi-lifecycle`
- Raw SKILL.md: https://api.skillmd.com/api/skills/gusbavia/pbi-lifecycle/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- License: MIT
- Author: gusbavia (https://skillmd.com/u/gusbavia)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/gusbavia/pbi-lifecycle

---


# Power BI Project Lifecycle · v0.3.3

End-to-end skill for the disk lifecycle of a Power BI delivery project. From an empty folder to a version-controlled, env-promotable project, with safety rails that protect non-git users from destructive mistakes.

## Why this skill exists

Power BI delivery has six recurring pain points the operator should not have to relearn each time:

1. The Power BI Desktop **MAX_PATH 256-char limit** — strict, undocumented at file level, instantly broken when Auto Date/Time auto-creates `DateTableTemplate_<GUID>.tmdl` files inside long client paths.
2. The **PBIR-Legacy vs PBIR-Modular** confusion — modular looks cleaner but Power BI Desktop converts to legacy on first save and themes don't auto-apply when generated as modular.
3. **`gh auth setup-git` is a separate step from `gh auth login`** — without it, `git push` fails after a successful login with the misleading "Invalid username or token" error.
4. **Auto Date/Time** is enabled by default in Power BI but considered an anti-pattern by every senior practitioner. The skill ships PBIPs with it disabled by default.
5. Power BI devs are often **not git users**. The skill's audience does not learn git just to use this. The skill explains how to inspect changes via the VS Code Source Control panel without typing a single git command.
6. **Theme propagation across projects** is manual today (copy JSON into each PBIP). The skill (Phase 4) automates this with per-project override semantics so a client rebrand is one command.

This skill captures every workaround once, locks it as a pre-flight check or runtime guard, and lets the operator focus on building reports.

## Generic positioning · MANDATORY for the agent running this skill

This skill ships to any Power BI practitioner. The agent running it must enforce strict neutrality. Violating any rule below is a defect.

1. **Do not infer or use the operator's personal context.** Never read auto-memory, prior conversation history, or session context to invent defaults, examples, or questions. The skill's behavior must be identical for the skill author, an external consultant, or a complete stranger.
2. **Do not reference any specific organization, client, product, project, or person** in any prompt, message, default value, example, or option. If unsure whether a name is generic, omit it.
3. **Do not ask any questions beyond the inputs defined in each Phase's Inputs section.** No project-type wizards, no scaffolding presets, no organization-specific naming taxonomies.
4. **Do not propose any folder, file, or convention beyond what is defined in the Hierarchy section.** The structure is fixed and exhaustive for this version.
5. **All placeholders in user-facing examples must be neutral** — `Client A`, `Project Name`, `My Power BI Project`. No real client names from any context.
6. **The skill is conversational but not creative.** Do not "be helpful" by adding steps, options, or branching logic. The defined inputs and the fixed structure are the entire surface area.

If the operator volunteers a specific client name like "Acme Corp", that is fine — it becomes the value of `clientName` for that run. The agent must not propose, suggest, or remember any specific name on its own.

## Forbidden pre-flight behaviors

Before, during, and after this skill runs, the agent must not perform any of the following:

1. **Do not list, scan, or inspect the operator's filesystem before asking the Clients root question.** No `pwd`, `ls`, `Get-ChildItem`, `dir`, or any equivalent on the current working directory or any directory the operator has not explicitly named. The skill's first interactive action is the **mode triage** prompt; the second is the relevant input question for the chosen mode.
2. **Do not propose candidate locations** based on what already exists on the operator's machine. Every path comes from the operator's typed answer.
3. **Do not auto-wire companion skills** (`pbi-model-doc`, `pbi-model-audit`, `pbi-theme-audit`, etc.) during any phase. Mention them in closing handoffs only as one-line pointers.
4. **Do not skip a Phase to "save time".** Phases run in declared order with explicit operator confirmation between them.

The agent's first message after the skill is invoked must be the **mode triage** prompt described in the next section, exactly. No greeting beyond that, no preamble, no context-gathering, no scanning of cwd.

## Mode triage · the first message the skill always sends

The skill is the lifecycle skill — multiple operations are available. The agent must always start by asking the operator which mode they want.

**First message — verbatim, in the operator's language (PT-BR or EN, match the input):**

```
What do you want to do?

  1. Bootstrap a new Power BI project folder structure
     (creates Client + Global Info + Global Theme + Power BI Repository
      with 01.Dev/02.Test/03.Prod env hubs and per-project subfolders;
      auto-creates a junction at C:\PBI-Clients\<slug>\ if path budget
      requires it)
     — AVAILABLE

  2. Scaffold a PBIP file inside an existing project folder
     (generates working .pbip in PBIR-Legacy from scratch with
      Auto Date/Time disabled, theme auto-applied if Global Theme
      has one, and short internal folder names to fit MAX_PATH 256)
     — AVAILABLE

  3. Initialize Git tracking for the client and push to a private
     GitHub repo (one repo per client, gh CLI flow with the three
     mandatory steps install / auth login / auth setup-git)
     — AVAILABLE

  4. Refresh the visual theme across all projects of a client
     (reads Global Theme, applies per-project theme.md overrides,
      writes new theme.json into each PBIP's RegisteredResources/,
      always backs up first, supports --dry-run preview)
     — AVAILABLE

  5. Promote a project to the next environment OR close out a project
     (Dev → Test or Test → Prod: copies 02_Build/, rewrites only
      catalog / http host / path / auth from the target env's source.json,
      AND generates a diff-findings report (same editorial style as
      pbi-model-doc) comparing source vs target so operator approves
      consistency before the merge.
      On Test → Prod and on close-project: ALSO generates a unified
      deployment lifecycle report covering the entire project history —
      timeline, commits, audit scores, promotion records, sign-off — for
      client handoff into 03_Docs/client_handoff/docs/.)
     — DESIGN LOCKED, IMPLEMENTATION QUEUED

  6. Help me read changes in VS Code (no git knowledge required)
     — AVAILABLE (educational walkthrough, no filesystem changes)

  7. Validate, audit, or document an existing project
     — Use companion skills: pbi-model-audit, pbi-theme-audit, pbi-model-doc

  8. Something else
     — Not in scope for this skill

Type 1–8.
```

**Routing rules:**

- Modes `1`, `2`, `3`, `4`, `6` → proceed to the corresponding Phase section below.
- Mode `5` → reply: *"That phase has the design locked but is not yet executable in this skill version. The agent could perform individual steps manually based on the design notes in this skill, but the automation is queued for v0.5 after real-PBIP calibration. Want a manual walkthrough or stop here?"* If the operator wants the manual walkthrough, present the algorithm from the corresponding phase section but ask for explicit confirmation at every mutation. Do not improvise beyond the documented design.
- Mode `7` → reply: *"Use the companion skill: `pbi-model-audit` to audit, `pbi-theme-audit` for the visual layer, `pbi-model-doc` to generate documentation. This skill (`pbi-lifecycle`) only handles folder lifecycle and version control."* Then stop.
- Mode `8` or off-list → reply: *"That's outside the scope of `pbi-lifecycle`. This skill only handles the disk lifecycle and version control of Power BI projects."* Then stop.
- If the operator's initial invocation already implies a specific mode (e.g. *"setup my new power bi project"* → mode 1, *"create the pbip"* → mode 2, *"init git for this project"* → mode 3), the agent may infer that mode and proceed directly, skipping the triage prompt.

The triage list is fixed and exhaustive for this version. The agent must not add, remove, or reword the eight options.

---

# Phase 1 · Folder Bootstrap (AVAILABLE)

Phase 1 is **strictly additive**. It only creates folders and net-new placeholder files. It never deletes, moves, renames, copies, or modifies anything that already exists.

## What Phase 1 does

1. Asks the operator for the absolute local path of the **Clients root** (must already exist).
2. Asks the **client name**. If the client folder is missing, creates it together with the shared client-level subtrees: `Global Info/`, `Global Theme/`, and `Power BI Repository/`.
3. Asks the **project name**. Creates the project folder under `Power BI Repository/01. Dev/<Project>/` with the per-project subfolders (`01_Context/`, `02_Build/`, `03_Docs/internal|client_handoff/audit|docs/`). Replicates the empty project shell into `02. Test/` and `03. Prod/` for future promotion.
4. Adds `.gitkeep` files in empty env folders and `Global Info/` so the structure persists in version control.
5. **Computes the path budget.** If the absolute path of the client root exceeds 80 characters, **automatically creates a NTFS junction** at `C:\PBI-Clients\<client-slug>\` pointing to the OneDrive/long path and tells the operator to use the junction path going forward. If ≤ 80 chars, no junction is created — the operator works directly at the original path.
6. Optionally accepts a `--theme <path>` flag (or interactive prompt) to seed the client's theme.json into `Global Theme/pbi-theme/theme/` from an existing source.

## Hierarchy created

The skill creates this exact structure. Names in `<…>` are operator-provided. All other names are fixed defaults — the agent must not rename them.

```
<Clients root>/                                   ← operator provides; must exist
  <Client>/                                       ← created if missing
    Global Info/                                  ← shared client info; .gitkeep placeholder
    Global Theme/                                 ← shared client visual identity
      logo/                                       ← operator manually places client logos
      brand-guideline/                            ← operator manually places brand guidelines
      pbi-theme/                                  ← output of pbi-theme companion skill
        theme/                                    ← .json (importable) + canvas SVG
        design-system/                            ← .pdf + .html documentation
        design-tokens/                            ← .css custom properties
        html-visual-style/                        ← DAX HTML visual examples
    Power BI Repository/                          ← env hub
      01. Dev/                                    ← Dev env (uses "01. " prefix with period and space)
        .gitkeep                                  ← preserves env folder when projects absent
        <Project>/                                ← created per project; the work happens here
          01_Context/                             ← project briefing (lowercase snake_case)
            scope.md                              ← scope template (placeholder)
            source.json                           ← per-env connection schema (env=dev)
            theme.md                              ← optional per-project theme override notes
            wireframe_to_data_mapping.md          ← placeholder for wireframe XLSX
          02_Build/                               ← where .pbip lives (Phase 2 target; empty after Phase 1)
          03_Docs/
            internal/                             ← internal-only outputs (gitignored by default)
              audit/                              ← auto-target for pbi-model-audit + pbi-theme-audit when invoked inside lifecycle
              docs/                               ← auto-target for pbi-model-doc when invoked inside lifecycle
            client_handoff/                       ← shareable with client (Git-tracked)
              audit/                              ← operator manually promotes approved audits here
              docs/                               ← operator manually promotes approved docs / manuals here
      02. Test/                                   ← Test env (replicated empty)
        .gitkeep
        <Project>/                                ← same internal subtree, empty until promoted
      03. Prod/                                   ← Prod env (replicated empty)
        .gitkeep
        <Project>/
```

## Critical naming rules (every one was paid for during calibration)

| Aspect | Rule | Why |
|---|---|---|
| Env folder prefix | `01. Dev/` (with period AND space) | Visual ordering + readability; do not use `01_Dev/` |
| Per-env folders | `01. Dev`, `02. Test`, `03. Prod` (numbered in this exact order) | Promotion path is left-to-right |
| Per-project subfolders | `01_Context`, `02_Build`, `03_Docs` (underscore, no space) | Distinguishes from env-level prefix |
| Files inside `01_Context/` | lowercase snake_case (`scope.md`, `source.json`, `theme.md`, `wireframe_to_data_mapping.md`) | Filesystem-friendly + cross-platform |
| Empty folders | Get a `.gitkeep` so they persist in git | Git does not track empty dirs |
| `Global Info/` and `Global Theme/` | Created at client level, shared across all projects | Theme + reference docs are client-wide |

## Pre-flight: path budget detection

Before creating folders, the agent computes the **absolute path length of the proposed client root**:

```
$clientPath = "$clientsRoot\$clientName"
$pathLength = $clientPath.Length
```

Decision tree:

| Path length | Action |
|---|---|
| ≤ 80 chars | No junction. Skill proceeds directly at the original path. Note in handoff: "Path budget is comfortable; no junction created." |
| 81–110 chars | Junction recommended. Prompt: *"Your client root is `$pathLength` chars long. Power BI Desktop's MAX_PATH limit is 256 chars total, and complex projects can grow paths quickly. Recommend creating a NTFS junction at `C:\PBI-Clients\<slug>\`. Create junction? (y/N)"* — default Y. |
| > 110 chars | Junction MANDATORY. Prompt: *"Your client root is `$pathLength` chars long. Without a junction at `C:\PBI-Clients\<slug>\`, Power BI Desktop will fail to save certain files. The junction is mandatory for this path."* — proceed to create. |

Junction creation (no admin required):

```powershell
$slug = $clientName.ToLower() -replace '[^a-z0-9]+','-' -replace '^-+|-+$',''
$junctionRoot = "C:\PBI-Clients"
if (-not (Test-Path $junctionRoot)) {
  New-Item -ItemType Directory -Path $junctionRoot | Out-Null
}
$junctionPath = "$junctionRoot\$slug"
if (Test-Path $junctionPath) {
  # If junction exists pointing elsewhere, abort with clear error.
  # Otherwise (target matches), reuse silently.
}
New-Item -ItemType Junction -Path $junctionPath -Target $clientPath
```

After junction creation, **the agent informs the operator explicitly**:

> Junction created at `C:\PBI-Clients\<slug>\` → `<original path>`. Always open `.pbip` files via the junction path. The OneDrive/SharePoint folder is the sync source of truth; the junction is your working entry point. Both stay in sync automatically.

## Cross-cutting · Companion skill auto-routing contract

Once a client is bootstrapped with this skill, the four companion skills auto-detect they're inside a lifecycle-managed structure and route their outputs to the right subfolder. This is the contract every skill in the IF Power BI Quality Suite respects:

| Companion skill | Detects lifecycle when invoked at | Auto-routes output to | Standalone fallback |
|---|---|---|---|
| `pbi-theme` (mode 2) | Any path under `<Client>/` | `<Client>/Global Theme/pbi-theme/{theme,design-system,design-tokens,html-visual-style}/` | Operator-chosen folder (mode 1) |
| `pbi-model-audit` (v0.2.1+) | A `.pbip` inside `<Client>/Power BI Repository/<env>/<Project>/02_Build/` | `<Project>/03_Docs/internal/audit/{YYYY-MM-DD}/` | `{project_root}/_outputs/audit/{YYYY-MM-DD}/` |
| `pbi-model-doc` (v0.2.2+) | Same | `<Project>/03_Docs/internal/docs/{YYYY-MM-DD}/` | `{project_root}/_outputs/doc/{YYYY-MM-DD}/` |
| `pbi-theme-audit` (v0.1.1+) | Same | `<Project>/03_Docs/internal/audit/{YYYY-MM-DD}/` (shared with model-audit, prefixed by tool) | `{project_root}/_outputs/theme-audit/{YYYY-MM-DD}/` |

### Detection algorithm (each companion skill runs this independently)

```
Walk up from the .pbip path. If the chain matches:
  02_Build/<Project>/<env>/Power BI Repository/<client>/
where <env> matches "01. Dev" | "02. Test" | "03. Prod"
→ LIFECYCLE_MODE = true
→ project_folder = parent of "02_Build"
→ output goes to $project_folder/03_Docs/internal/{audit|docs}/{YYYY-MM-DD}/
```

If lifecycle is not detected, the companion skill falls back to its standalone behavior. **Independent use of any companion skill remains fully supported** — this contract enriches the experience inside lifecycle without making lifecycle a hard dependency.

### Internal vs client_handoff promotion (manual operator action)

`03_Docs/internal/` is gitignored by the lifecycle's `.gitignore` template. Audit and doc runs accumulate there as work-in-progress. When an audit or doc is **approved for client sharing**, the operator manually copies the artifact from `internal/` to `client_handoff/`. The act of promoting is a deliberate, traceable decision recorded as a git commit:

```
[<Project>] Promote audit report to client_handoff
[<Project>] Promote model doc to client_handoff
```

This separation is intentional: companion skills generate freely into `internal/`, operator curates into `client_handoff/`. No skill ever writes directly into `client_handoff/`.

## Cross-cutting · BYO theme

If the operator already has a `theme.json` (from `pbi-theme` skill, TabularEditor 3, an existing PBIP, or a brand consultant), Phase 1 supports seeding it into `Global Theme/pbi-theme/theme/` so Phase 2 picks it up automatically.

Three input modes:

| Input | Behavior |
|---|---|
| `--theme <path>` flag with existing JSON | Skill validates JSON, copies to `Global Theme/pbi-theme/theme/<filename>.json` |
| Interactive prompt during Phase 1 | *"Do you have an existing theme.json for this client? (path / no / skip)"* — `path`: provide the path. `no` or `skip`: leave empty; Phase 2 uses Power BI default |
| Pre-populated `Global Theme/pbi-theme/theme/*.json` (typical when `pbi-theme` already ran) | Skill auto-detects + uses |

**Validation on import (mandatory before copy):**

1. JSON parse validity — fail fast on broken JSON.
2. **Lowercase enum sweep** — find every `"left"`, `"top"`, `"bottom"`, `"right"`, `"center"`, `"auto"`, `"bottomonly"`, `"fit"` and verify they are CamelCase (`"Left"`, `"Top"`, etc.). Power BI silently fails the theme if any enum is lowercase.
3. **`name` field length** — must be ≤ 16 characters. Power BI uses this name to derive internal filenames during save (with a 17-char hash + `.json` suffix).
4. **Hex color check** — every value matching `/^#[0-9a-fA-F]{6}$/` is valid; warn on `#XXX` shorthand or invalid characters.

If any validation fails, the agent reports the specific error and offers to either fix the JSON in place (with operator confirmation) or stop.

## Hard safety rules

Phase 1 is **strictly additive**. The skill must never affect anything that already exists on disk.

**Forbidden operations** in Phase 1, no exception:

| Operation | Why forbidden |
|---|---|
| `Remove-Item`, `rm`, `del`, `rmdir` | Phase 1 never deletes. |
| `Move-Item`, `mv` | Phase 1 never moves. |
| `Rename-Item`, `ren` | Phase 1 never renames. |
| `Copy-Item`, `cp`, `xcopy`, `robocopy` of operator content | Phase 1 never copies the operator's content (the BYO theme copy is the only exception, and only after explicit operator confirmation of source path). |
| Any write to an existing file (overwrite, append, replace) | Phase 1 only creates new folders and net-new placeholder files. |
| `git init`, `git add`, `git commit`, any Git command | Git belongs to Phase 3. |
| `gh repo create` or any GitHub operation | GitHub belongs to Phase 3. |
| Creating folders or files the operator did not confirm | Every creation requires explicit operator confirmation of the full creation plan. |

**Only allowed write operations:**

1. `New-Item -ItemType Directory -Path "<absolute path>"` — only when `Test-Path "<absolute path>"` returns `$false`.
2. `New-Item -ItemType File -Path "<absolute path>" -Value "<placeholder content>"` — only for the placeholder template files defined in this skill, when `Test-Path` returns `$false`.
3. `New-Item -ItemType Junction -Path "<short path>" -Target "<long path>"` — only after path budget detection prompts the operator.
4. `Copy-Item <theme-source> -Destination "<Global Theme>/pbi-theme/theme/"` — only after BYO theme validation succeeds and the operator confirms the source path.

If the operator asks for any forbidden operation during Phase 1, refuse and explain which Phase handles it.

## Phase 1 execution algorithm

```
STEP 1 · Clients root
  Ask: absolute local path of Clients root (e.g., C:\Clients\ or OneDrive sync path).
  Validate:
    - Test-Path returns $true → continue
    - Reject SharePoint URLs (https://*.sharepoint.com/...)
    - Reject relative paths (.\, ..\)
    - Reject drive-letter-only paths (C:\)
    - If $false → STOP, tell operator the path does not exist locally

STEP 2 · Client
  Ask: client name (string).
  Compute:
    $clientPath = "$clientsRoot\$clientName"
    $pathLength = $clientPath.Length
  
  Path budget decision (see "Pre-flight: path budget detection" above).
  If junction needed → confirm + create.
  
  Build creation plan for client-level folders:
    $clientPath\Global Info\          (+ .gitkeep)
    $clientPath\Global Theme\
    $clientPath\Global Theme\logo\
    $clientPath\Global Theme\brand-guideline\
    $clientPath\Global Theme\pbi-theme\
    $clientPath\Global Theme\pbi-theme\theme\
    $clientPath\Global Theme\pbi-theme\design-system\
    $clientPath\Global Theme\pbi-theme\design-tokens\
    $clientPath\Global Theme\pbi-theme\html-visual-style\
    $clientPath\Power BI Repository\
    $clientPath\Power BI Repository\01. Dev\           (+ .gitkeep)
    $clientPath\Power BI Repository\02. Test\          (+ .gitkeep)
    $clientPath\Power BI Repository\03. Prod\          (+ .gitkeep)
  
  Test-Path each. Skip those that exist. Present plan + ask y/N.
  On y: New-Item Directory + .gitkeep files for missing entries.
  
  BYO theme prompt:
    Ask: "Do you have an existing theme.json? (path / no / skip)"
    On path: validate (JSON, enums, name length, hex) → copy into Global Theme/pbi-theme/theme/
    On no/skip: leave folder empty; Phase 2 will use Power BI default
  
  If $clientPath already fully scaffolded → report "client already set up" and continue to STEP 3.

STEP 3 · Project
  Ask: project name (string).
  Compute:
    $devProjectPath  = "$clientPath\Power BI Repository\01. Dev\$projectName"
    $testProjectPath = "$clientPath\Power BI Repository\02. Test\$projectName"
    $prodProjectPath = "$clientPath\Power BI Repository\03. Prod\$projectName"
  
  If Test-Path $devProjectPath returns $true → STOP. Tell operator the project folder already exists,
    ask them to choose a different name. Phase 1 must never touch an existing project folder.
  
  Build full creation plan for the project subtree (Dev populated, Test+Prod replicated empty):
    Dev:
      $devProjectPath\01_Context\
        scope.md                              (placeholder)
        source.json                           (placeholder, "environment": "dev")
        theme.md                              (placeholder)
        wireframe_to_data_mapping.md          (placeholder)
      $devProjectPath\02_Build\               (empty; Phase 2 target)
      $devProjectPath\03_Docs\internal\audit\
      $devProjectPath\03_Docs\internal\docs\
      $devProjectPath\03_Docs\client_handoff\audit\
      $devProjectPath\03_Docs\client_handoff\docs\
    Test:
      $testProjectPath\01_Context\
        source.json                           (placeholder, "environment": "test")
        + same other files as Dev
      $testProjectPath\02_Build\
      $testProjectPath\03_Docs\... (same subtree)
    Prod:
      $prodProjectPath\01_Context\
        source.json                           (placeholder, "environment": "prod")
        + same other files as Dev
      $prodProjectPath\02_Build\
      $prodProjectPath\03_Docs\... (same subtree)
  
  Present plan + ask y/N.
  On y: New-Item Directory + New-Item File for every entry.

STEP 4 · Closing handoff
  Print:
    - Absolute path of the Dev project folder (use junction path if junction was created)
    - ASCII tree of the per-project subtree
    - Junction info if created
    - One-line note: "Phase 1 complete. To scaffold the .pbip file, run mode 2 (PBIP scaffold).
      To version-control this client, run mode 3 (Git init). For audits and docs, see
      pbi-model-audit / pbi-theme-audit / pbi-model-doc."
```

## Placeholder file contents

### `01_Context/source.json` (per env, env = `"dev"` | `"test"` | `"prod"`)

```json
{
  "environment": "dev",
  "description": "Connection parameters that change between environments. Phase 5 (env promotion) reads this file from the target env and rewrites the corresponding values in expressions.tmdl. Never store secrets here.",
  "connections": [
    {
      "name": "<connection-name>",
      "type": "<Web | SQL | Databricks | OData | File | Other>",
      "url": "<https://server-or-endpoint>",
      "catalog": "<database-or-catalog>",
      "schema": "<schema-or-folder-or-lakehouse-path>",
      "auth": {
        "method": "<OAuth | Anonymous | Key | UsernamePassword>",
        "credentialsHandledBy": "Power BI Desktop / Service (never store secrets in this file)"
      }
    }
  ]
}
```

### `01_Context/scope.md`, `theme.md`, `wireframe_to_data_mapping.md`

Empty single-line placeholders with H1 title only. The operator fills these as the project starts.

### `theme.md` per-project override format (Phase 4 contract)

If a project requires a theme override on top of the Global Theme, the operator edits `01_Context/theme.md` to declare the override:

```yaml
---
inherits: ../../../../Global Theme/pbi-theme/theme/<theme-filename>.json
overrides:
  dataColors[0]: "#7C3AED"
  neutral: "#7C3AED"
  reason: "Client requested purple primary for this project to avoid color collision with another product"
---
```

Phase 4 (theme refresh) reads this and applies overrides on top of the Global Theme during refresh. If no `overrides:` block exists, the project inherits the Global Theme verbatim.

### `.gitkeep`

Empty file. Just exists so git tracks the parent folder.

---

# Phase 2 · PBIP Scaffold from scratch (AVAILABLE)

Phase 2 generates a working `.pbip` file inside `<Project>/02_Build/` from scratch, using the **PBIR-Legacy** format. The result opens cleanly in Power BI Desktop with the client theme auto-applied (if Global Theme has one) and Auto Date/Time disabled by default.

This was calibrated against multiple iterations during 2026-05-04 — the recipe below is exact and exhaustive.

## Pre-flight checks

Before generating any file, the agent verifies all of the following:

1. The client root exists and contains `Global Info/`, `Global Theme/`, `Power BI Repository/01. Dev/<Project>/02_Build/`. If any is missing → instruct operator to run Phase 1 first. Stop.
2. `<Project>/02_Build/` is **empty**. If a `.pbip` or any subfolder already exists there → STOP, ask operator if they want to delete the existing scaffold first (Phase 2 never overwrites).
3. **Path budget check** — if `<Project>/02_Build/` absolute path > 100 chars and no junction exists → warn operator, recommend running Phase 1 with junction or moving the client root.
4. **MAX_PATH headroom calculation:**
   - Base path: `<absolute path to 02_Build>` 
   - Plus deepest expected file: `Report\StaticResources\RegisteredResources\<theme-filename>.json` (~78 chars)
   - Plus deepest TMDL: `SemanticModel\definition\tables\<TableName>.tmdl` (~52 chars + table name)
   - Total must be ≤ 256 chars assuming reasonable table names (≤ 30 chars). If headroom < 30 chars → strongly recommend junction.
5. **BYO theme detection:** check `Global Theme/pbi-theme/theme/*.json`:
   - If exactly one `.json` exists → use it, validate enums + name length before copying.
   - If multiple → ask operator which to use.
   - If none → ask: "No theme found. Generate one now via the `pbi-theme` companion skill, drop a JSON manually into `Global Theme/pbi-theme/theme/`, or scaffold the PBIP without a custom theme (Power BI default)? (theme/skill/skip)"

## LOCKED Phase 2 recipe (every aspect calibrated empirically)

Generate a `.pbip` directory tree with this exact structure:

```
<Project>/02_Build/
  <Project>.pbip                                    ← wrapper, schema pbipProperties/1.0.0
  Report/                                           ← short folder name (NOT {Project}.Report/)
    .platform                                       ← gitIntegration/2.0.0, fresh logicalId GUID
    definition.pbir                                 ← schema definitionProperties/2.0.0, version 4.0
    report.json                                     ← AT ROOT (NOT under definition/), PBIR-Legacy format
    StaticResources/
      RegisteredResources/<theme-filename>.json     ← copy of the master theme from Global Theme
      SharedResources/BaseThemes/CY26SU04.json      ← built-in base theme (must be present)
  SemanticModel/                                    ← short folder name (NOT {Project}.SemanticModel/)
    .platform                                       ← fresh logicalId GUID, displayName=project name
    definition.pbism                                ← schema definitionProperties/1.0.0, version 4.2
    definition/
      database.tmdl                                 ← compatibilityLevel: 1600
      model.tmdl                                    ← MUST include __PBI_TimeIntelligenceEnabled = 0
      expressions.tmdl                              ← env parameters with fresh lineageTag GUIDs
```

### Critical recipe constraints

| Aspect | Rule | Why |
|---|---|---|
| **Format** | PBIR-Legacy (NOT PBIR-modular) | Power BI Desktop converts modular → legacy on save; theme does not auto-apply when generated as modular |
| **Encoding** | UTF-8 **without BOM** for ALL files: `.pbip`, `.platform`, `.pbir`, `.pbism`, `.json`, `.tmdl` | BOM breaks Power BI Desktop parsing silently |
| **Theme `name` field** | ≤ 16 chars | Power BI derives internal filenames (with 17-char hash + `.json` suffix) from this; longer names create paths that exceed 256 chars |
| **Enum values** | Case-sensitive: `Left`, `Top`, `Bottom`, `Right`, `Center`, `Auto`, `BottomOnly`, `Fit` | NEVER lowercase — Power BI silently rejects the theme |
| **Internal folder names** | Use short `Report/` and `SemanticModel/` | Saves ~17 chars of path per nesting level vs `{Project}.Report/` |
| **MAX_PATH** | 256 chars | Power BI Desktop limit, stricter than Windows 260; pre-flight check mandatory |
| **Section name** | 20 hex chars random per project | Generated fresh per scaffold |
| **GUIDs** | Fresh `logicalId` per `.platform`, fresh `lineageTag` per expression | Each scaffold gets unique IDs |
| **Base theme** | Always copy `BaseThemes/CY26SU04.json` (or current Power BI default) into `SharedResources/` | Required even when custom theme is also present |
| **Custom theme** | Copy from `Global Theme/pbi-theme/theme/` into `Report/StaticResources/RegisteredResources/` | Source-of-truth lives in Global Theme |
| **`compatibilityLevel`** | 1600 in `database.tmdl` | NOT 1567 |
| **`model.tmdl` annotation** | `annotation __PBI_TimeIntelligenceEnabled = 0` | **MANDATORY.** Disables Auto Date/Time. Without this, Power BI auto-creates `DateTableTemplate_<GUID>.tmdl` (60+ chars) for every date column when data is loaded — instantly explodes MAX_PATH 256 |
| **`model.tmdl` annotation** | `annotation PBI_ProTooling = ["DevMode"]` | Required for the file to be recognized as Dev Mode |
| **`definition.pbism`** | `version: "4.2"` | Schema version |
| **`definition.pbir`** | `version: "4.0"`, schema `definitionProperties/2.0.0` | Schema version |
| **`report.json`** | At root of `Report/`, NOT in `definition/` | PBIR-Legacy convention |
| **`report.json` config** | Stringified inner JSON with `themeCollection` (`baseTheme` + `customTheme`), `settings` block | The customTheme `name` references the filename in `RegisteredResources/` |
| **`report.json` resourcePackages** | Numeric types: `1` = RegisteredResources, `2` = SharedResources, `201` = CustomTheme, `202` = BaseTheme | Power BI rejects string types |

### Theme-optional behavior (graceful degradation)

Phase 2 must NOT fail when no custom theme exists. Pre-flight check:

```
if Test-Path "<Client>/Global Theme/pbi-theme/theme/*.json":
    → copy theme into PBIP RegisteredResources, write full themeCollection (baseTheme + customTheme)
else:
    → omit customTheme from themeCollection
    → omit RegisteredResources entry from resourcePackages (or leave items array empty)
    → PBIP opens with Power BI default theme (CY26SU04 only)
    → emit warning: "No custom theme found. PBIP opens with Power BI default. Run pbi-theme later, then re-run this skill in mode 4 (refresh-theme) to propagate."
```

This makes `pbi-theme` genuinely optional — clients without it can still generate PBIPs.

## Phase 2 execution algorithm

```
STEP 1 · Pre-flight (see "Pre-flight checks" above; abort on any failure).

STEP 2 · Generate fresh GUIDs and IDs
  $reportLogicalId  = New-Guid
  $smLogicalId      = New-Guid
  $sectionName      = (-join (1..20 | ForEach-Object { '0123456789abcdef'[(Get-Random -Maximum 16)] }))
  $expressionGuids  = @{
    Source_Url    = New-Guid
    Source_Schema = New-Guid
    Source_Type   = New-Guid
    Source_Auth_Method = New-Guid
  }

STEP 3 · Compute target paths
  $buildPath  = "<Client>/Power BI Repository/01. Dev/<Project>/02_Build"
  $reportPath = "$buildPath/Report"
  $smPath     = "$buildPath/SemanticModel"
  $regResPath = "$reportPath/StaticResources/RegisteredResources"
  $sharedResPath = "$reportPath/StaticResources/SharedResources/BaseThemes"

STEP 4 · Determine theme handling (BYO theme detection from pre-flight)
  $themeFilename = (filename of selected JSON or null)
  $themeSourcePath = (path to selected JSON or null)

STEP 5 · Confirm with operator
  Present:
    - Project name
    - Target build path
    - Theme handling (with theme / without)
    - List of files about to be created (~10-12 files)
  Ask: "Generate? (y/N)"

STEP 6 · Generate files (each as UTF-8 WITHOUT BOM)
  
  6.1  Create folders: $reportPath, $smPath, $regResPath, $sharedResPath, "$smPath/definition"
  
  6.2  Write <Project>.pbip:
       {
         "$schema": "https://developer.microsoft.com/json-schemas/fabric/item/pbiProperties/1.0.0/schema.json",
         "version": "1.0",
         "artifacts": [
           { "report": { "path": "Report" } }
         ]
       }
  
  6.3  Write Report/.platform with $reportLogicalId
  6.4  Write Report/definition.pbir
  6.5  Write Report/report.json (with $sectionName, themeCollection, resourcePackages)
  6.6  Copy CY26SU04.json into $sharedResPath (the bytes of this file are deterministic; embed as a resource bundled with the skill)
  6.7  If $themeSourcePath: validate (enums, name length, hex) → copy to $regResPath
  
  6.8  Write SemanticModel/.platform with $smLogicalId
  6.9  Write SemanticModel/definition.pbism (version 4.2)
  6.10 Write database.tmdl (compatibilityLevel 1600)
  6.11 Write model.tmdl with __PBI_TimeIntelligenceEnabled = 0 + PBI_ProTooling = ["DevMode"]
  6.12 Write expressions.tmdl with the 4 source parameters + fresh lineageTags

STEP 7 · Validate output
  - Test-Path on every generated file
  - Test JSON parse on .pbip, .platform, .pbir, .pbism, report.json, theme.json
  - Lowercase enum sweep on theme.json (must find ZERO lowercase enums)
  - File-by-file BOM check (must be ZERO BOMs)
  - Path budget recheck on every generated path

STEP 8 · Closing handoff
  Print:
    - Absolute path of the .pbip
    - "Open with Power BI Desktop. Theme should auto-apply on first open. If you load data
       and Power BI prompts you about Auto Date/Time, decline (this skill disabled it by design).
       To version-control this work, run mode 3 (Git init)."
```

## Phase 2 hard safety rules

1. **Never overwrite an existing PBIP.** If `02_Build/` is non-empty, stop.
2. **Never modify Global Theme.** Phase 2 only READS the theme JSON; copies it into the PBIP's RegisteredResources/.
3. **Never write BOM.** Use explicit `[System.IO.File]::WriteAllText($path, $content, [System.Text.UTF8Encoding]::new($false))` in PowerShell.
4. **Never run Power BI Desktop programmatically** to "validate" the generated PBIP — only file-level validation. Operator opens it manually.

## Phase 2 reference script

The calibrated reference algorithm lives in `Gus bavia brand/_step-pbip-gen-from-scratch.ps1` from the calibration session. Future versions of this skill should bundle this script under `pbi-lifecycle/references/phase2-pbip-scaffold.ps1` so the agent can invoke it directly. For v0.3.0, the agent generates files inline using the recipe above.

---

# Phase 3 · Git Tracking + GitHub repo (AVAILABLE)

Phase 3 initializes git at the **client root** (one repo per client, all projects share the same repo), creates a private GitHub repo via `gh` CLI, makes the snapshot-zero commit, and establishes the project-scoped commit pattern for ongoing work.

This was validated end-to-end on 2026-05-04 against a disposable test project.

## Why one repo per client (locked architecture decision)

| Ganho | Why |
|---|---|
| Scaffold zero pra novo projeto | Cria pasta dentro de `Power BI Repository/01. Dev/` e pronto, já tá versionado |
| Global Theme convive com os projetos | Mudou cor da marca? 1 commit alcança todos projetos do cliente |
| Visão consolidada do cliente | `git log` mostra atividade de todos devs em todos projetos do cliente |

Custo (e mitigação): histórico mistura projetos, mitigado pela convenção `[ProjectName] msg` no commit + `git log -- "<path>"` filtra. Validado.

When `1 repo por projeto` would make sense (NOT the default): if a single project must be shared with an external party without exposing other projects of the same client. Phase 3 v0.3.0 does not implement this — clients with that need stay on the locked default.

## Pre-flight checks (every gotcha calibrated)

The agent verifies in this exact order:

### Pre-flight 1 · Power BI Desktop is closed

```powershell
Get-Process -Name 'PBIDesktop' -ErrorAction SilentlyContinue
```

If a process exists → ask operator to close it. Power BI Desktop holds locks on `.pbip` files; git operations during an open session can corrupt the repo.

### Pre-flight 2 · gh CLI installed

```powershell
$ghPath = "C:\Program Files\GitHub CLI\gh.exe"
if (-not (Test-Path $ghPath)) {
  # Install via winget
  winget install --id GitHub.cli --accept-source-agreements --accept-package-agreements -e
}
```

If install fails → instruct operator to install manually from https://cli.github.com/ and re-run.

### Pre-flight 3 · gh authenticated

```powershell
& $ghPath auth status
```

If exit code ≠ 0 → run interactive login:

```
The skill prints to operator:
"Run this in a fresh PowerShell:
  gh auth login
Answer the prompts:
  - Where: GitHub.com
  - Protocol: HTTPS
  - Authenticate Git: Yes
  - Auth method: Login with a web browser
  - Open the URL printed, paste the device code, authorize.
Tell me when done."

Operator confirms → recheck `gh auth status`.
```

### Pre-flight 4 · gh auth setup-gi

…(truncated)
