Skip to content

Lab 03: Repository and path instructions

Time
About 25 minutes
Level
intro
Platforms
Windows 11, macOS
Copilot surfaces
VS Code
You'll use
instructions, path-instructions, agents-md
Starts from
None needed
Ends at
None needed
Last verified
Not yet run end to end on Windows 11

Works on macOS

Nobody has run this lab end to end on Windows 11 yet. The steps follow the current docs for each tool, but menu labels may differ. The verify command at the end is the source of truth, and feedback is welcome.

Before you build custom agents, you need to know what context Copilot already reads. Repository instructions set the rules for every chat. Path instructions add file-specific rules only when their applyTo pattern matches. AGENTS.md gives multi-agent tools a shared guide. This lab shows how to prove which files shaped an answer by checking References in VS Code.

  • Repository instructions: .github/copilot-instructions.md, automatically applied to chat requests in the workspace.
  • Path instructions: .github/instructions/*.instructions.md, applied when applyTo matches the files in context or the task.
  • AGENTS.md: a root agent guide read by VS Code and other agent tools when enabled.
  • References: the expandable list in a VS Code Chat response that shows instruction files, prompt files, and other context used.
  • Customization diagnostics: a VS Code view that helps when instructions are not applied.
  • Finish Lab 01 and open the repo in VS Code.
  • Copilot Chat works in VS Code.
  • Leave Power BI Desktop closed. You don’t need it in this lab.

Open each file in VS Code:

  • Keyboard: Ctrl+P (macOS: Cmd+P), type the file name, press Enter.
  • Command Palette: Ctrl+Shift+P (macOS: Cmd+Shift+P), run File: Open File.
  • Menu: File > Open File.

Start with the repository instructions:

.github/copilot-instructions.md (lines 1-34)
# 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 |
|---|---|
| Set up the Python environment | `pwsh ./scripts/Initialize-DevEnvironment.ps1` |
| Validate everything (no Fabric tenant needed) | `pwsh ./scripts/Invoke-Validation.ps1` |
| TMDL lint only | `pwsh ./scripts/Invoke-Validation.ps1 -Only tmdl` |

Then read the shared agent guide:

AGENTS.md (lines 1-35)
# 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) |

You should see: the repo map, synthetic data boundaries, validation commands, and a rule that agents should not read expected answers while answering business questions.

  1. Open Copilot Chat: Ctrl+Alt+I (macOS: Ctrl+Cmd+I), or View > Chat.
  2. Select Ask or a normal chat mode, not a custom agent.
  3. Ask: What naming convention should measures follow here?
  4. When the answer finishes, expand References, Used references, or the references list shown under the response. The exact label can vary by VS Code version.

VS Code settings in this repo make instruction usage visible:

.vscode/settings.json (lines 1-10)
{
// Copilot customization: these are the defaults today, set explicitly so the lab behaves the same
// if a default changes.
"chat.useAgentsMdFile": true,
"chat.includeApplyingInstructions": true,
"chat.includeReferencedInstructions": true,
"chat.useHooks": true,
// Start MCP servers only after you've run them once yourself (Lab 04).
"chat.mcp.autostart": "onlyNew",

You should see: the answer mentions format strings, descriptions, display folders, or rules/naming-conventions.yaml. The References list includes .github/copilot-instructions.md and may include AGENTS.md.

Step 3. Compare path instructions for TMDL and Python

Section titled “Step 3. Compare path instructions for TMDL and Python”

Open a TMDL file first:

  1. Open fabric/ContosoPharmacy.SemanticModel/definition/tables/fact_rx_fill.tmdl.
  2. Ask Copilot: How should I write a description for this column?
  3. Expand the response References.
.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.

Then compare with Python:

  1. Open tools/python/pharmacy_demo/labcheck/checks.py.
  2. Ask Copilot: What style should I follow if I add a helper function here?
  3. Expand the response References.
.github/instructions/python.instructions.md
---
applyTo: "tools/python/**/*.py"
description: Conventions for the pharmacy_demo Python tools.
---
# Python tools
- Target Python 3.12 or later. Use `pathlib.Path` for every path and open text files with `encoding="utf-8"`. Write files with `newline="\n"` so output matches on Windows and Linux.
- Never hardcode `/usr`, `/bin`, `/tmp`, drive letters, or a user's home folder. Find the repo root with `pharmacy_demo.paths.repo_root()`.
- The command-line entry point is `pharmacy_demo/cli.py`. Every PowerShell script in `scripts/` wraps one subcommand, so keep flags stable.
- Output meant for other tools goes to stdout as JSON when `--json` is set. Human output is short, one line per finding.
- Exit codes: 0 pass, 1 findings or failure, 2 usage or setup error.
- Dependencies are pinned with hashes in `tools/python/requirements*.txt`. Don't add a package without updating those files.
- Add or update tests in `tools/python/tests/`. Run `.venv/bin/python -m pytest -q tools/python/tests` (on Windows, `.venv\Scripts\python`) and `ruff check tools/python`.
- Modules under `pharmacy_demo/modelgen/` and `samples.py` hold lab answers. They're for maintainers only; agents helping learners shouldn't read them.

You should see: the TMDL answer references tmdl.instructions.md and talks about /// descriptions, tabs, lineage tags, and validation. The Python answer references python.instructions.md and talks about Python 3.12, pathlib.Path, UTF-8, exit codes, and tests.

Step 4. Create a path instructions file for rules

Section titled “Step 4. Create a path instructions file for rules”

The lab check requires a new .github/instructions/*.instructions.md file whose applyTo pattern matches real repo files. Create rules.instructions.md for the YAML rules under rules/.

Terminal window
@'
---
applyTo: 'rules/**'
description: Rules for editing lab convention YAML files.
---
# Rule files
- Keep rule IDs stable because scripts, skills, and lab checks refer to them.
- Explain why a rule exists before changing thresholds or required objects.
- Run `pwsh ./scripts/Invoke-Validation.ps1 -Only customizations` after editing rules.
'@ | Set-Content -Path .github/instructions/rules.instructions.md -Encoding utf8

Open rules/naming-conventions.yaml, then ask Copilot: What should I be careful about before changing a convention rule?

You should see: the response uses your new guidance, and References includes .github/instructions/rules.instructions.md. The file has non-empty frontmatter with applyTo: 'rules/**', and that pattern matches real files under rules/.

Step 5. Review the shipped path instructions

Section titled “Step 5. Review the shipped path instructions”

Open each shipped file under .github/instructions/. They are scoped to different parts of the repo:

  • tmdl.instructions.md for semantic model TMDL.
  • pbir.instructions.md for report JSON.
  • python.instructions.md for Python tools.
  • powershell.instructions.md for scripts.
  • evaluation.instructions.md for business questions and run files.
  • site.instructions.md for the lab site.
.github/instructions/pbir.instructions.md
---
applyTo: "fabric/**/*.Report/**"
description: Rules for the Contoso Pharmacy report in PBIR format.
---
# PBIR report files
- The report is PBIR: one `visual.json` per visual under `definition/pages/<page>/visuals/<visual>/`. Folder names are stable IDs; don't rename them.
- A field reference has an `Entity` (table name) and a `Property` (column or measure name). Both must match the semantic model exactly, including spaces and case.
- When the model renames a table, column, or measure, update every `Entity`, `Property`, and `queryRef` that uses the old name. Also update `nativeQueryRef` and any `displayName` that shows the old name.
- Don't change visual positions, sizes, or formatting unless the task asks for it.
- Keep JSON valid and keep the `$schema` line. Two-space indentation, as Desktop writes it.
- After editing, run `pwsh ./scripts/Test-ReportBindings.ps1`. The `pbir-rebinding` skill has the full procedure.
.github/instructions/powershell.instructions.md
---
applyTo: "scripts/**/*.ps1"
description: Conventions for the lab's PowerShell 7 scripts.
---
# PowerShell scripts
- Target PowerShell 7.2 or later (`#Requires -Version 7.2`). Scripts must run on Windows, macOS, and Linux under `pwsh`.
- Start every script with comment-based help (`.SYNOPSIS`, `.DESCRIPTION`, `.PARAMETER`, `.EXAMPLE`) and `[CmdletBinding(PositionalBinding = $false)]`. The lab site generates its script reference from the help.
- Take list parameters as comma-separated strings and split them inside the script, so `-Rule C07,C08` works the same from any shell.
- Dot-source `_Common.ps1` for `Get-RepoRoot`, `Get-VenvPython`, and `Invoke-PharmacyDemo`. Build paths with `Join-Path` and quote them with `-LiteralPath`, because learners clone into folders with spaces.
- Never install software. Check for it and print the install command instead.
- Pass arguments to external tools as arrays. Use `Invoke-Git -Arguments @(...)` from `_Checkpoints.ps1` for Git.
- Wrap single results in `@()` before indexing, because PowerShell unrolls one-item arrays.
- Guard Windows-only code with `$IsWindows` and exit 0 with a clear SKIP message elsewhere.
- Run `Invoke-ScriptAnalyzer -Path scripts -Recurse -Settings PSScriptAnalyzerSettings.psd1` and `Invoke-Pester -Path scripts/tests` before you commit.
.github/instructions/evaluation.instructions.md
---
applyTo: "evaluation/**"
description: Rules for business questions, run files, and grading.
---
# Evaluation files
- `evaluation/expected/*.json` are generated by the DuckDB oracle from the synthetic CSVs. Never edit them. Maintainers regenerate them with `pwsh ./scripts/Update-ExpectedResults.ps1`.
- When you answer questions as the `question-tester` agent, don't open `evaluation/questions.yaml` or anything under `evaluation/expected/`. Use `pwsh ./scripts/Get-BusinessQuestions.ps1`, which leaves out the reference answers.
- A run file lives at `evaluation/runs/<label>.json` and must match `evaluation/run.schema.json`. Labels are lowercase with hyphens, for example `local-baseline`.
- Each answer's `rows` use the `answer_columns` names from the questions output, in that order. Put every assumption you made in `assumptions`.
- Never write the `grade` block yourself. `pwsh ./scripts/Grade-QuestionRun.ps1 -Run <label>` writes it.
- `sample-*.json` runs are illustrative and committed. `local-*.json` runs are yours and gitignored.
- Scoring rules are in `evaluation/rubric.md`.
.github/instructions/site.instructions.md
---
applyTo: "site/**"
description: Rules for the self-paced lab site (Astro Starlight).
---
# Lab site
- Lab pages live in `site/src/content/docs/labs/NN-slug.mdx` and follow the ten-section template. Load the `lab-page-authoring` skill before writing or editing a lab page.
- Show repository files with the `RepoFile` component, never by pasting their contents, so the site can't drift from the repo.
- Every lab ends with a `VerifyStep` whose `lab` matches an entry in `lab/checks.yaml`. Step headings use ids `step-1`, `step-2`, and so on, because lab check hints link to them.
- Windows PowerShell 7 comes first in every tab set. Mark Windows-only steps with `PlatformBadge`.
- No analytics, external scripts, fonts, or images from other hosts. `npm run check` fails the build if the output references another site.
- Write in plain, direct language: short sentences, no em or en dashes, no emojis. Screenshots show synthetic data and a demo account only.
- Run `npm run check` and `npm run build` in `site/` before you commit.

You should see: each file has frontmatter with applyTo, followed by short rules for that path. They are instructions, not scripts, so they guide Copilot but don’t enforce anything by themselves.

Step 6. Check instruction diagnostics and settings

Section titled “Step 6. Check instruction diagnostics and settings”

If a reference is missing, use VS Code’s diagnostics before editing files blindly.

  1. Open the Command Palette: Ctrl+Shift+P (macOS: Cmd+Shift+P), or View > Command Palette.
  2. Run Chat: Configure Instructions to see available instruction files.
  3. Run Chat: Open Customizations if your VS Code version has the Agent Customizations editor.
  4. In the Chat view, open the More Actions menu, then look for Diagnostics when an instruction file is not applied.
  5. Confirm these settings are enabled in .vscode/settings.json: chat.includeApplyingInstructions, chat.includeReferencedInstructions, and chat.useAgentsMdFile.

The VS Code docs say to use References when an applyTo pattern may not be matching, and to check those settings when instructions aren’t applied.

You should see: your workspace instruction files listed, with source locations. No JSON or YAML parse errors should appear for rules.instructions.md.

Step 7. Compare which surfaces read which instructions

Section titled “Step 7. Compare which surfaces read which instructions”

Read the customization reference before later labs add agents, prompts, skills, MCP, hooks, and code review.

  1. Open Copilot customization.
  2. Open Copilot surfaces.
  3. Compare VS Code Chat, Copilot CLI, Copilot cloud agent, and Copilot code review.
  4. Note which surfaces use repository instructions, path instructions, and AGENTS.md.

You should see: repository instructions and path instructions are shared context, while enforcement comes from validation scripts, CI, hooks, and code review.

Terminal window
git status --short .github/instructions
git diff -- .github/instructions/rules.instructions.md

You should see: one new file, .github/instructions/rules.instructions.md, with the applyTo: 'rules/**' frontmatter and the three rules you added.

Run this from the repo root in a PowerShell 7 terminal (Terminal > New Terminal in VS Code):

Terminal window
pwsh ./scripts/lab/Test-LabProgress.ps1 -Lab 03

When everything is done you should see:

Expected output
PASS L03-instructions You added a path instructions file whose applyTo matches repo files
Lab 03: 1 of 1 checks passed.

Each FAIL line prints a hint and a link back to the step on this page that fixes it. The command exits with code 0 when every required check passes.

CheckWhat it looks atIf it fails
L03-instructionsYou added a path instructions file whose applyTo matches repo filesCreate .github/instructions/rules.instructions.md with applyTo: 'rules/**'. Go to step 4
References don’t show any instruction files

Check .vscode/settings.json for chat.includeApplyingInstructions, chat.includeReferencedInstructions, and chat.useAgentsMdFile. Then reload VS Code with Developer: Reload Window from the Command Palette.

Your new instructions file doesn’t apply

Check the frontmatter exactly. The lab check expects a new .github/instructions/*.instructions.md file, an applyTo value, non-empty body text, and a glob that matches real repo files. rules/** matches the files in rules/.

Copilot answers with generic Power BI advice

Make sure the repo folder is the VS Code workspace root. If you opened only fabric/, VS Code won’t find .github/copilot-instructions.md or AGENTS.md at the root.

The Command Palette labels differ from the lab

VS Code Copilot UI labels move. Use the closest command with Instructions, Customizations, or Diagnostics in the name, then confirm the file names in References.

What is the difference between `.github/copilot-instructions.md` and a `.instructions.md` file?

Show the answer

`copilot-instructions.md` is always-on for the repo. `.instructions.md` files apply by glob or task context.

Repository instructions apply broadly. Path instructions add more specific rules when their frontmatter matches the task or files.

How do you verify which instruction files shaped a VS Code Chat answer?

Show the answer

Expand References or Used references under the chat response.

VS Code shows the instruction files and other context used by the response in its References list.

Why are instructions not enough to protect generated data or expected answers?

Show the answer

Instructions guide the agent, but hooks, validation, and CI enforce rules deterministically.

Instructions are advisory context. The repo also uses hooks, validation scripts, and CI for hard checks.

You read the repo’s always-on instructions, confirmed path instructions in VS Code References, created a new rules-scoped instructions file, and learned where to diagnose missing instruction context. Next you connect MCP so agents can inspect the Power BI model with tools.

Next: Lab 04: Connect Power BI Modeling MCP

  • Add one more rule to rules.instructions.md, ask a rules question again, and see whether the answer changes.
  • Open Copilot customization and map instructions, skills, prompts, agents, hooks, and MCP to the labs that use them.