Skip to content

Copilot customization

This repo uses every major Copilot customization primitive that the lab teaches. The generated catalogs are here: Agents, Skills, Prompts, and Scripts.

The behavior below was checked against VS Code and GitHub Copilot docs in September 2026. Preview features can move, so recheck the docs before you use this as a production policy.

PropertyValue
What it isAlways-on repository custom instructions.
Location.github/copilot-instructions.md
Frontmatter used hereNone.
Loads whenCopilot works in this repository.
LabLab 03
.github/copilot-instructions.md (lines 1-31)
# Copilot instructions for the Contoso Pharmacy lab
This repository is a hands-on lab. It teaches GitHub Copilot's agent features (instructions, prompt files, skills, custom agents, handoffs, hooks, MCP, the Copilot CLI, the cloud agent, and code review) using a Power BI semantic model stored as code.
## Synthetic data only
Every row of data here is synthetic and generated from a fixed seed. Never add real patient, customer, employee, or tenant data, names, identifiers, or screenshots. Never add credentials, tokens, tenant IDs, workspace IDs, or connection strings. Use placeholders such as `${input:...}` or `${env:...}`.
## Repository map
| Path | What it holds |
|---|---|
| `fabric/ContosoPharmacy.pbip` | Power BI Project. Open it in Power BI Desktop (Windows only). |
| `fabric/ContosoPharmacy.SemanticModel/definition/` | The semantic model as TMDL. This is what most tasks change. |
| `fabric/ContosoPharmacy.Report/definition/` | The report as PBIR JSON. Field references must match model names. |
| `fabric/ai-prep/` | Drafts for Power BI's Prep data for AI (AI instructions, schema, synonyms, verified answers). |
| `data/generated/` | Synthetic CSVs. Generated; never edit by hand. |
| `evaluation/` | Business questions, run files, and the grading rubric. Expected answers are hidden from agents. |
| `rules/` | Naming conventions and required model objects, read by the checks and the skills. |
| `tools/python/pharmacy_demo/` | Python tools behind every script: data generation, lint, conventions, grading, lab checks, hooks. |
| `scripts/` | PowerShell 7 entry points. Learners run these. |
| `lab/` | Lab verification contract (`checks.yaml`) and checkpoint manifest (`checkpoints.json`). |
| `site/` | The self-paced lab site (Astro Starlight), published to GitHub Pages. |
| `.github/agents`, `skills`, `prompts`, `instructions`, `hooks` | The Copilot customizations the labs teach. |
## Commands
Windows PowerShell 7 is the primary shell. The same scripts run in `pwsh` on macOS and Linux.
| Task | Command |
|---|---|

Use this for broad facts: synthetic data, repo layout, validation commands, and boundaries that apply to most tasks.

Path instructions live in .github/instructions/*.instructions.md. VS Code docs define applyTo, description, and optional name. GitHub docs require applyTo for path-specific instructions and also document excludeAgent for GitHub surfaces. This repo uses applyTo and description.

FileapplyToLab
tmdl.instructions.mdfabric/**/*.tmdlLab 03
pbir.instructions.mdfabric/**/*.Report/**Lab 07
python.instructions.mdtools/python/**/*.pyLab 01
powershell.instructions.mdscripts/**/*.ps1Lab 01
evaluation.instructions.mdevaluation/**Lab 05
site.instructions.mdsite/**Lab 14 and maintainers
.github/instructions/tmdl.instructions.md
---
applyTo: "fabric/**/*.tmdl"
description: Rules for editing the Contoso Pharmacy semantic model in TMDL.
---
# TMDL files
- Indent with tabs, one level per nesting depth. Properties of an object sit one tab deeper than the object line.
- Put descriptions on the line directly above the object as `/// text`. Keep them under 200 characters, because Power BI Copilot reads only the first 200.
- Quote names that contain spaces or special characters with single quotes: `column 'Store Key'`, `measure 'MoM %'`. In DAX, refer to columns as `'Table Name'[Column Name]` and to measures as `[Measure Name]`.
- Never change or remove a `lineageTag`. When you add an object, leave `lineageTag` out; Desktop adds one when it saves.
- When you rename a column, keep `sourceColumn` pointing at the CSV header. Rename references in `relationships.tmdl`, measures, sort-by columns, hierarchies, `cultures/en-US.tmdl`, and the report.
- Set `summarizeBy: none` on keys, IDs, years, month numbers, and other numbers that must not be added up. Only the columns listed as `additive` in `rules/naming-conventions.yaml` may summarize.
- Hide keys and sort helpers with `isHidden`.
- Every measure needs a description, a `formatString`, and a `displayFolder` from the list in `rules/naming-conventions.yaml`.
- Relationships to Date, Store, and Medication are many-to-one and single direction. Don't add `crossFilteringBehavior: bothDirections`.
- Don't edit `expressions.tmdl` unless the task is about the data source. The `DataFolderPath` parameter must stay a placeholder path, never a personal path.
- After editing, run `pwsh ./scripts/Invoke-Validation.ps1 -Only tmdl`. Load the `tmdl-authoring` skill for syntax details.
PropertyValue
What it isAgent instructions shared by multiple coding agents.
LocationAGENTS.md
Frontmatter used hereNone.
Loads whenAgent-capable surfaces read repository agent instructions.
LabLab 03
AGENTS.md
# Agent guide
Instructions for any coding agent working in this repository. GitHub Copilot also reads `.github/copilot-instructions.md`, which has the full repository map.
## What this repo is
A self-paced lab that improves a Power BI semantic model (PBIP with TMDL and PBIR) using GitHub Copilot's agent features. All data is synthetic. It never needs a Microsoft Fabric tenant.
## Set up and validate
```powershell
pwsh ./scripts/Initialize-DevEnvironment.ps1 # creates .venv from hashed lock files
pwsh ./scripts/Invoke-Validation.ps1 # data, expected answers, runs, TMDL lint, bindings, customizations, hygiene
pwsh ./scripts/Invoke-Validation.ps1 -Tests # plus pytest and Pester
```
On macOS or Linux the same commands work in `pwsh`. Python tools can also run directly: `.venv/bin/python -m pharmacy_demo validate`.
## Boundaries
- Never edit generated files: `data/generated/**`, `evaluation/expected/**`, `fabric/**/.pbi/cache.abf`, `fabric/**/.pbi/localSettings.json`, or `fabric/**/Copilot/**`.
- Never read expected answers or reference DAX while answering the business questions.
- Never add real people, customers, credentials, tenant IDs, or workspace IDs.
- Never force push or change global Git config.
- Repository hooks in `.github/hooks/guardrails.json` enforce the first, second, and fourth rules for Copilot CLI, VS Code, and the cloud agent.
## Custom agents
| Agent | Use it to |
|---|---|
| `question-tester` | Answer the five business questions from the model and grade the run |
| `model-assessor` | Review the model against the conventions without changing it |
| `model-improver` | Fix names, descriptions, relationships, measures, and AI prep drafts |
| `model-reviewer` | Review a model pull request |
| `lab-author` | Write or update lab pages (maintainers) |
Agent files live in `.github/agents/`. Skills they load live in `.github/skills/`.
## Done means
- `pwsh ./scripts/Invoke-Validation.ps1` passes.
- Renamed objects are rebound in the report (`pwsh ./scripts/Test-ReportBindings.ps1`).
- New model objects have descriptions, and new measures have format strings and display folders.

Use it for short operating rules. Keep deep domain knowledge in skills.

Agent Skills are folders with a SKILL.md. VS Code docs require name and description. The description should say what the skill does and when to use it.

SkillTeaches
pharmacy-model-conventionsNames, descriptions, measures, relationships, and AI readiness rules.
tmdl-authoringSafe TMDL edits.
pbir-rebindingReport visual rebinding after renames.
business-question-evaluationRun files, grading, and comparison.
prep-data-for-aiAI instruction, schema, synonym, and verified-answer drafts.
lab-page-authoringMaintainer rules for lab pages.
.github/skills/pharmacy-model-conventions/SKILL.md (lines 1-35)
---
name: pharmacy-model-conventions
description: House conventions for the Contoso Pharmacy semantic model, covering business names, descriptions, hidden keys, summarization, display folders, required relationships and measures, and the AI-readiness checklist. Use when you assess, review, or change model names, descriptions, measures, relationships, or the date table, or when you need the standard business name for a source column.
---
# Contoso Pharmacy model conventions
These conventions make the model easy for people and for AI tools (Power BI Copilot, Fabric data agents, and coding agents) to read. They are checked by rules C01 to C15.
The rules themselves are data, not prose. Read them from:
- `rules/naming-conventions.yaml`: name pattern, forbidden abbreviations, description length, hidden-column patterns, additive columns, raw-code columns, display folders.
- `rules/required-model-objects.yaml`: required tables, relationships, date table settings, the twelve required measures by display folder, default labels, and the AI prep requirements.
## Get the current findings
Run the checker instead of reviewing by eye. It's deterministic and it's the same code the lab checks use.
```powershell
pwsh ./scripts/Test-ModelConventions.ps1 # table of findings
pwsh ./scripts/Test-ModelConventions.ps1 -Json # for agents
pwsh ./scripts/Test-ModelConventions.ps1 -Rule C07,C08
```
On macOS or Linux, `pwsh` runs the same scripts. Every finding has a rule, severity, object, finding, and fix.
## Rules
| Rule | Title | What good looks like |
|---|---|---|
| C01 | Business-friendly names | Title Case words with spaces. No underscores or abbreviations like `rx`, `dt`, `nm`, `qty`. Use the names in [the data dictionary](references/data-dictionary.md). |
| C02 | Descriptions present | Every table, visible column, and measure has a `///` description. |
| C03 | Description length | 20 to 200 characters. Say what it is, its grain or unit, and when to use it. |
| C04 | Keys and helpers hidden | Surrogate keys, foreign keys, `Fill ID`, and sort-by number columns are hidden. |
| C05 | Default summarization | `summarizeBy: none` everywhere except `Quantity` and `Days Supply` (sum) and `Processing Time (min)` (average). |
| C06 | Raw codes decoded | `Fill Type Code` (N or R) is hidden, and a visible `Fill Type` column shows New or Refill. |

Skills load on demand when the description matches the task. See Skills for the generated list.

Custom agents live in .github/agents/*.agent.md. VS Code docs list name, description, argument-hint, tools, handoffs, model, target, and other fields. GitHub cloud agent docs require description, allow tools, and document mcp-servers and target. This repo uses name, description, argument-hint, tools, and handoffs.

.github/agents/model-assessor.agent.md (lines 1-12)
---
name: model-assessor
description: Reviews the Contoso Pharmacy semantic model against the team conventions and AI-readiness checklist, without changing it, and writes prioritized findings to out/assessment.md. Use before improving the model or to explain why AI answers are weak.
argument-hint: Optional focus, for example "names only" or "measures and time intelligence"
tools: ['read', 'search', 'execute', 'edit', 'todo', 'powerbi-modeling-mcp/*']
handoffs:
- label: Fix these findings
agent: model-improver
prompt: Fix the findings in out/assessment.md, highest severity first. Start with names and descriptions, and rebind the report after any rename.
send: false
---
# Model assessor
AgentWhat it doesLab
question-testerAnswers and grades the five questions.Lab 05
model-assessorFinds model gaps without editing model files.Lab 06
model-improverEdits TMDL, PBIR, measures, relationships, and AI prep drafts.Lab 07
model-reviewerReviews model and report diffs.Lab 12
lab-authorMaintainer page-writing agent.Lab 14

Handoffs switch to another agent and prefill a prompt. This repo uses send: false so you review the prompt before it runs.

Prompt files live in .github/prompts/*.prompt.md. VS Code docs list optional name, description, argument-hint, agent, model, and tools. This repo uses name, description, agent, and sometimes argument-hint.

.github/prompts/improve-names-and-descriptions.prompt.md
---
name: improve-names-and-descriptions
description: Rename tables and columns to business names, add descriptions, hide keys, set summarization and display folders, then rebind the report.
agent: model-improver
---
Improve names and descriptions in the Contoso Pharmacy semantic model. Use the `pharmacy-model-conventions`, `tmdl-authoring`, and `pbir-rebinding` skills.
Scope for this change:
1. Rename every table and visible column to the business name in the conventions skill's data dictionary. Keep `sourceColumn` values unchanged. Update `model.tmdl`, `relationships.tmdl`, table file names, and any DAX.
2. Rebind every report visual to the new names, and replace implicit aggregations with measures if the measures already exist.
3. Add a `///` description to every table and visible column: what one row is, or what the value means, with an example value where it helps. Keep each under 200 characters.
4. Hide surrogate keys and sort helpers. Set `summarizeBy: none` on keys, codes, years, and other non-additive numbers.
Out of scope: new measures, relationship changes, and AI prep drafts.
After each group of edits run:
```powershell
pwsh ./scripts/Invoke-Validation.ps1 -Only tmdl,bindings
pwsh ./scripts/Test-ModelConventions.ps1 -Rule C01,C02,C03,C04,C05
```
Finish when the lint and bindings pass and C01 to C05 report no findings.

Use prompt files for repeatable scoped tasks such as /assess-model, /improve-names-and-descriptions, /add-core-measures, /answer-business-questions, and /review-model-pr. See Prompts.

Hooks live in .github/hooks/*.json. They run shell commands at agent lifecycle events. This repo uses hooks to block protected files, block unsafe commands, log sessions, and lint TMDL after edits.

.github/hooks/guardrails.json
{
"version": 1,
"hooks": {
"sessionStart": [
{
"type": "command",
"bash": "./scripts/hooks/run-hook.sh session-start",
"powershell": "pwsh -NoProfile -File ./scripts/hooks/run-hook.ps1 -HookEvent session-start",
"cwd": ".",
"timeoutSec": 20
}
],
"preToolUse": [
{
"type": "command",
"bash": "./scripts/hooks/run-hook.sh pre-tool-use",
"powershell": "pwsh -NoProfile -File ./scripts/hooks/run-hook.ps1 -HookEvent pre-tool-use",
"cwd": ".",
"timeoutSec": 20
}
],
"postToolUse": [
{
"type": "command",
"bash": "./scripts/hooks/run-hook.sh post-tool-use",
"powershell": "pwsh -NoProfile -File ./scripts/hooks/run-hook.ps1 -HookEvent post-tool-use",
"cwd": ".",
"timeoutSec": 30
}
],
"sessionEnd": [
{
"type": "command",
"bash": "./scripts/hooks/run-hook.sh session-end",
"powershell": "pwsh -NoProfile -File ./scripts/hooks/run-hook.ps1 -HookEvent session-end",
"cwd": ".",
"timeoutSec": 10
}
]
}
}

VS Code workspace MCP lives in .vscode/mcp.json. Copilot CLI uses ~/.copilot/mcp-config.json by default. GitHub cloud agent and code review use repository MCP settings on GitHub, with a root mcpServers object.

.vscode/mcp.json
{
// MCP servers for the Contoso Pharmacy lab. No secrets or tenant details belong in this file.
// Versions are pinned; update them deliberately and re-run Lab 04 after an update.
"servers": {
// Power BI Modeling MCP: connects to the model open in Power BI Desktop, or to the PBIP folder.
// If you installed the Power BI Modeling MCP VS Code extension instead, disable this entry so
// the tools don't appear twice.
"powerbi-modeling-mcp": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@microsoft/powerbi-modeling-mcp@0.5.0-beta.13", "--start"]
},
// Optional. Fabric MCP: Microsoft Fabric documentation and item definitions. Its docs tools
// work without a Fabric tenant. Start it from the MCP view only if you want it.
"fabric-mcp": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@microsoft/fabric-mcp@1.4.0", "server", "start", "--mode", "all"]
}
}
}
config/cloud-agent-mcp.template.json
{
"$comment": "Template for Copilot cloud agent MCP configuration. Paste the mcpServers object into your repository's Settings > Copilot > Coding agent > MCP configuration. It contains no secrets. The cloud agent runs on Linux without Power BI Desktop. Power BI Modeling MCP ships a Linux build and can open a PBIP folder, but this lab hasn't verified that on the cloud agent, so it's read-only here and the cloud agent labs don't depend on it.",
"mcpServers": {
"powerbi-modeling-mcp": {
"type": "local",
"command": "npx",
"args": [
"-y",
"@microsoft/powerbi-modeling-mcp@0.5.0-beta.13",
"--start",
"--readonly"
],
"tools": [
"*"
]
},
"fabric-mcp": {
"type": "local",
"command": "npx",
"args": [
"-y",
"@microsoft/fabric-mcp@1.4.0",
"server",
"start",
"--mode",
"all"
],
"tools": [
"*"
]
}
}
}

Use ${input:...}, environment variables, or COPILOT_MCP_ secrets and variables. Never commit secrets.

NeedUseWhy
Repo-wide facts and commandscopilot-instructions.mdAlways-on and simple.
File-family rules.instructions.mdapplyTo keeps rules scoped.
Cross-agent operating guidanceAGENTS.mdMultiple agent tools read it.
Reusable domain workflowSkillLoads on demand with references.
Role and tool boundaryCustom agentPersona and tools move together.
Repeatable slash commandPrompt fileIt scopes one task.
Deterministic policyHookCode can deny or ask.
Live toolsMCPTools reach Desktop, docs, or APIs.