PBI verify loop
One command replaces the pbir desktop refresh → Start-Sleep <guess> → pbir desktop screenshot cycle:
python "${CLAUDE_PLUGIN_ROOT}/skills/pbi-verify-loop/scripts/verify_page.py" \
"MyReport.Report/ReportSection1.Page" -o "<scratchpad>/page.png"
The script refreshes the report from disk, then captures repeatedly (every 3s, up to 45s) until two consecutive screenshots are byte-identical. That is the "settled" signal. It prints JSON (output, captures, stable, changed_from_baseline) and exits 0 when stable, 2 on timeout (the last capture is still written, so inspect it anyway).
changed_from_baseline compares the final capture against a screenshot taken before the refresh, so a two-identical-captures loop cannot settle on the pre-edit canvas. false means the refresh produced a pixel-identical page: the edit may not have reached disk, or Desktop did not pick it up, so check that before reporting the change as verified. It is null under --no-refresh, where there is no baseline to compare against.
Options
| Flag |
Use |
--model |
Also re-apply the TMDL model (refresh -m); needed after model.tmdl/measure edits |
--no-refresh |
Just settle+capture (e.g. after the user changed something in Desktop) |
--compare mockup.png |
Diff final capture vs a reference; writes <out>-vs-ref.png side-by-side and reports diff_ratio + diff_bbox |
--region x,y,w,h |
Crop both images to a region before comparing (verify one visual, ignore the rest) |
--scale 1-3 |
Render scale (default 2) |
--timeout / --interval |
Stability polling knobs (default 45s / 3s) |
Workflow
- Edit PBIR/TMDL files on disk (the
pbip plugin's tmdl and pbir-format skills cover the hand-authoring rules and gotchas).
- Run
pbir validate if visual.json was touched.
- Run this script; Read the output PNG and judge the change visually before telling the user it's done.
- For mockup-driven work (Claude Design), pass
--compare with the mockup and check diff_ratio, but always eyeball the side-by-side too; pixel diffs can't judge intent.
Preconditions & gotchas
- Power BI Desktop must be running with the report open, and the preview feature "external tool access to Power BI Desktop through secure local APIs" enabled (Options → Preview features, restart Desktop). Check with
pbir desktop list. If it errors or shows nothing, that preview is off: tell the user to enable it and restart Desktop rather than retrying. Where it stays off, reports:desktop-screen-capture screenshots the Desktop window directly, and where Desktop is unavailable at all, render the published report server-side with ExportTo (fabric-cli:fabric-cli, references/reports.md).
pbir desktop list first, always: if the instance shows Unsaved: yes, a refresh reloads from disk and can clobber the user's unsaved in-Desktop tweaks, so use --no-refresh or ask them to save first.
- Page path form is
Report.Report/PageName.Page (folder names from definition/pages/), not display names. Omit the page to capture the first page.
- Full-flag reference for
pbir desktop: the pbir-cli skill's references/cli-reference.md (Desktop Operations section). Key gotchas: the screenshot output flag is -o/--output (there is no --out), and --settle applies only with --all.
- If the capture never stabilizes (exit 2), something is animating or Desktop is stuck refreshing. Check
pbir desktop list for unsaved/busy state instead of raising the timeout blindly.
1---2name: pbi-verify-loop3description: Visually verify Power BI report edits against a running Power BI Desktop instance. Refresh the canvas from disk, wait until rendering is stable (no guessed sleeps), screenshot the page, and optionally diff against a mockup. Use after ANY edit to PBIR visual.json / pages / theme when Desktop is open, and whenever the user asks to "check the report", "verify the change", "screenshot the page", or a mockup comparison is needed. Replaces hand-rolled refresh + Start-Sleep + screenshot loops.4---56# PBI verify loop78One command replaces the `pbir desktop refresh` → `Start-Sleep <guess>` → `pbir desktop screenshot` cycle:910```bash11python "${CLAUDE_PLUGIN_ROOT}/skills/pbi-verify-loop/scripts/verify_page.py" \12 "MyReport.Report/ReportSection1.Page" -o "<scratchpad>/page.png"13```1415The script refreshes the report from disk, then captures repeatedly (every 3s, up to 45s) until two consecutive screenshots are byte-identical. That is the "settled" signal. It prints JSON (`output`, `captures`, `stable`, `changed_from_baseline`) and exits 0 when stable, 2 on timeout (the last capture is still written, so inspect it anyway).1617`changed_from_baseline` compares the final capture against a screenshot taken *before* the refresh, so a two-identical-captures loop cannot settle on the pre-edit canvas. `false` means the refresh produced a pixel-identical page: the edit may not have reached disk, or Desktop did not pick it up, so check that before reporting the change as verified. It is `null` under `--no-refresh`, where there is no baseline to compare against.1819## Options2021| Flag | Use |22|---|---|23| `--model` | Also re-apply the TMDL model (`refresh -m`); needed after model.tmdl/measure edits |24| `--no-refresh` | Just settle+capture (e.g. after the user changed something in Desktop) |25| `--compare mockup.png` | Diff final capture vs a reference; writes `<out>-vs-ref.png` side-by-side and reports `diff_ratio` + `diff_bbox` |26| `--region x,y,w,h` | Crop both images to a region before comparing (verify one visual, ignore the rest) |27| `--scale 1-3` | Render scale (default 2) |28| `--timeout` / `--interval` | Stability polling knobs (default 45s / 3s) |2930## Workflow31321. Edit PBIR/TMDL files on disk (the `pbip` plugin's `tmdl` and `pbir-format` skills cover the hand-authoring rules and gotchas).332. Run `pbir validate` if visual.json was touched.343. Run this script; **Read the output PNG** and judge the change visually before telling the user it's done.354. For mockup-driven work (Claude Design), pass `--compare` with the mockup and check `diff_ratio`, but always eyeball the side-by-side too; pixel diffs can't judge intent.3637## Preconditions & gotchas3839- Power BI Desktop must be running with the report open, and the preview feature **"external tool access to Power BI Desktop through secure local APIs"** enabled (Options → Preview features, restart Desktop). Check with `pbir desktop list`. If it errors or shows nothing, that preview is off: tell the user to enable it and restart Desktop rather than retrying. Where it stays off, `reports:desktop-screen-capture` screenshots the Desktop window directly, and where Desktop is unavailable at all, render the published report server-side with `ExportTo` (`fabric-cli:fabric-cli`, `references/reports.md`).40- **`pbir desktop list` first, always**: if the instance shows `Unsaved: yes`, a refresh reloads from disk and can clobber the user's unsaved in-Desktop tweaks, so use `--no-refresh` or ask them to save first.41- Page path form is `Report.Report/PageName.Page` (folder names from `definition/pages/`), not display names. Omit the page to capture the first page.42- Full-flag reference for `pbir desktop`: the `pbir-cli` skill's `references/cli-reference.md` (Desktop Operations section). Key gotchas: the screenshot output flag is `-o/--output` (there is no `--out`), and `--settle` applies only with `--all`.43- If the capture never stabilizes (exit 2), something is animating or Desktop is stuck refreshing. Check `pbir desktop list` for unsaved/busy state instead of raising the timeout blindly.