Worked example — end to end

Build an executive KPI dashboard, step by step.

Eight steps from a raw orders table to a signed-off one-pager. Each step shows the command to run and exactly what lands on disk.

The brief: the CFO wants a one-page executive summary from a raw orders table — revenue, margin and cash against target, exception-first, with RAG indicators. Using the C-Suite archetype (S-heavy SCAN, ~5 visuals, status card top-left).

$ 
Produces
Why it matters
The result — a C-suite one-pager
Executive Summary — FY Performance Pre-filtered to your portfolio · as at period close 1 exception REVENUE £48.2m ▲ 6.1% vs target GROSS MARGIN 37.4% ▲ 1.2 pts vs target DAYS SALES O/S 58d ▼ 9d over target NEW ACCOUNTS 142 — in line with plan Revenue vs Target — trailing 12 months ━ actual ╌ target Revenue by segment Enterprise 45% Mid-market 25% SMB 18% Public 12% Exceptions to act on DSO 58d — collections in EMEA slipping Margin dip in SMB — discounting up 3pts Enterprise renewals ahead of plan

Status band of four RAG KPI cards (one exception, flagged red), a revenue-vs-target trend, a margin donut, and an exceptions list — the SCAN pattern for a C-suite audience.

06Playbook

Start from where you actually are.

The walkthrough above is the full lifecycle. Most real work starts mid-stream — you already have a model, or you only need a theme. These are the common entry points, each a complete route from what you have to what you need.

Theme only I just need an on-brand, accessible theme.json

The fastest useful thing AtelierBID does. No model, no project, no AI step required.

  1. Run the deterministic builder with your brand colours:
    node scripts/build-theme.js --brand "#2A6FDB,#E8743B" --out ./out/theme --colors 10
  2. Check the palette does not collide with the reserved RAG status roles. Data colours must stay ≥ ΔE 15 from red/amber/green, or a "bad" number will read as healthy:
    node scripts/check-palette.js "#2A6FDB,#E8743B"
  3. Import in Power BI Desktop: View → Themes → Browse for themes.

Want it derived from a brand description rather than hex codes? Run /AtelierBID:build-theme in a session and describe the brand — you also get a palette rationale, font-colour choice, and a design critique scored 0–100.

Raw table I have a flat source table and need a proper model

The classic starting point: one wide orders-style table that needs to become a star schema.

  1. Ground first, so generation binds to your real columns rather than inventing them:
    node scripts/ground-schema.js load --use-sample
  2. Run /AtelierBID:model-star-schema in a session and paste your DDL — or drive the CLI directly:
    node scripts/model-star-schema.js \
      --describe "Sales by Date, Product, Region with Revenue, OrderCount" \
      --out ./tables --validate --preview
  3. Wire relationships, then check for anti-patterns:
    node scripts/model-relationships.js --tables ./tables --out relationships.tmdl
    node scripts/optimise-model.js --model ./tables

You get SQL DDL, a Mermaid ERD, DAX measure stubs, and a validation report scored against nine Kimball rules. The validator feeds fixes back to the designer on retry.

Mockup → PBIR I have an approved layout and need a real report page

This is the step that normally forces re-authoring by hand. The bridge is a shared mockup IR — one JSON contract that both the preview and the PBIR converter read, so what a stakeholder approves is exactly what gets generated.

  1. Compose a page and emit a reviewable mockup with the IR embedded:
    node scripts/design-report.js --compose --emit-mockup mockup.html --apply-tokens
  2. Optionally render an interactive prototype from the same IR for sign-off, handing the IR back out:
    node scripts/prototype-report.js --ir composed-ir.json --out ./prototype \
      --emit-ir approved-ir.json
  3. Convert the approved IR into a structurally-valid PBIR page inside your PBIP:
    node scripts/design-report.js --from-ir approved-ir.json --pbip ./projects/Q3Sales
    node scripts/validate-pbip.js --project ./projects/Q3Sales

The converter will not overwrite a file it did not write — existing pages are guarded behind an ownership sentinel. Unmapped visual types warn and skip rather than failing the whole run.

Copilot I need an existing model to answer Q&A reliably

Copilot and Q&A answer badly when a model has no descriptions, no synonyms, and exposed key columns. This is the fix, and it works on a model you did not build.

  1. Audit first — see the gaps before changing anything:
    node scripts/ai-readiness.js audit --model ./tables --out audit.json
  2. Enrich, then apply surface hygiene (hides keys, disambiguates clashing names via displayName without renaming physical columns):
    node scripts/ai-readiness.js enrich   --model ./tables   --out ./enriched
    node scripts/ai-readiness.js hygiene  --model ./enriched --out ./hygiene
  3. Add a linguistic schema and verified answers, then confirm the TMDL is still valid:
    node scripts/ai-readiness.js linguistic       --model ./enriched --out linguistic.json
    node scripts/ai-readiness.js verified-answers --model ./enriched --out va.json
    node scripts/validate-pbip.js --model ./enriched

Every sub-command exits 0 and records gaps in the artefact content rather than the exit code, so audit && grep chains keep running.

Inherited I inherited a model and need to understand it

Point the read-only tools at someone else's TMDL. Nothing here mutates your model.

  1. Generate a data dictionary straight from the TMDL — regenerable, gaps flagged:
    node scripts/document-model.js --model ./tables --out data-dictionary.md
  2. Get a ranked, result-preserving optimisation report:
    node scripts/optimise-model.js --model ./tables
    node scripts/optimise-dax.js   --model ./tables
  3. Audit it against the best-practice rule library:
    node scripts/best-practice.js scaffold
    node scripts/best-practice.js audit --model ./tables

DAX rewrites are dry-run by default and gated behind an explicit --apply. Offline, the equivalence verdict reports engine: none rather than claiming a proof it cannot make.

Sign-off I need something clickable to show a stakeholder

A static mockup gets nodded at; a clickable prototype gets argued with — which is what you actually want before building.

  1. Run /AtelierBID:prototype-report in a session with your report spec, or:
    node scripts/prototype-report.js --out ./prototype --sample
  2. Open prototype.html in a browser. You get real Chart.js charts, KPI cards with variance, dark mode and a responsive grid.
  3. When it is approved, hand the IR forward with --emit-ir and follow the Mockup → PBIR recipe above.
Viewing a prototype needs internet — the page loads Chart.js from a CDN. Generating every artefact, including IR → PBIR conversion, does not.
07When it goes wrong

Troubleshooting.

Exit 127 · "command not found" · "npm is not recognized"

Node is not on your shell's PATH. On Git Bash and default PowerShell, C:\Program Files\nodejs is often missing. Fix for the session — Bash: export PATH="/c/Program Files/nodejs:$PATH" · PowerShell: $env:PATH = 'C:\Program Files\nodejs;' + $env:PATH

A prototype opens blank, or the charts are missing

No internet. The prototype HTML loads Chart.js from a CDN, so viewing it needs a connection. The file itself is a complete, standalone artefact — and nothing in the generation path, including IR → PBIR, needs the network.

"brand colour collides with the reserved RAG status role"

Working as intended. A data colour within ΔE 15 of the red/amber/green status roles would make a failing number read as healthy. Pick a brand colour further from the status palette, or check candidates first with node scripts/check-palette.js.

A run stopped part-way through

Resume it. Every skill run scaffolds output/<run-id>/ and writes a checkpoint at each pipeline step. Re-invoke the same skill and it picks up from the last completed checkpoint rather than starting over.

A visual from my mockup did not appear in the PBIR page

Unmapped visual type. The converter maps IR visual types through a fixed table; anything unmapped warns and skips rather than failing the whole page. Check the warnings the converter printed, and the VISUAL_TYPE_MAP coverage.

"will not overwrite" when regenerating a report page

Ownership guard. The converter only overwrites files it wrote itself, identified by a sentinel in the PBIP settings. If you hand-edited the page, move or delete it first — the guard is what stops your manual work being silently destroyed.

08Vocabulary

Glossary.

The file formats and terms the documentation assumes.

PBIP
Power BI Project — the folder-based, text-first save format. A container that opens in Power BI Desktop and holds the semantic model and report as readable files rather than a binary .pbix.
TMDL
Tabular Model Definition Language — the human-readable text format for the semantic model: tables, columns, relationships, measures, partitions.
PBIR
Power BI Enhanced Report format — the JSON representation of report pages and visuals. What AtelierBID generates from an approved mockup IR.
IR
The shared mockup intermediate representation (kind: atelierbid/mockup-ir). A self-describing JSON contract carrying the grid, KPI strip and per-visual type. Both the human preview and the PBIR converter read it, so approval and generation cannot drift apart. Additive-only.
SCAN
The report-design framework used throughout: Status, Context, Analysis, Narrative. Each audience archetype weights these zones differently — a C-suite page is S-heavy, an analyst page is A-heavy.
RAG roles
The reserved red / amber / green semantic status colours. Data colours must stay ≥ ΔE 15 away from them so a status reading is never ambiguous.
ΔE
Perceptual colour distance. Two colours below ΔE 15 are close enough that viewers may confuse them — the threshold the palette guard enforces.
Grounding contract
The registered schema-of-record. Generation binds to it; anything off-schema is surfaced as a proposed addition rather than silently invented.
Offline floor
The guarantee that all generation and validation work with zero auth, network or cost. Live read/validate is opt-in; publish and refresh are always manual.
Emit-only
Scripts that write reviewable source text but never execute it. author-sql.js never connects to a database; author-m.js never evaluates M.