Skip to content

Commit 7a268b8

Browse files
authored
Add QA Checks to generation/edit, insipred by Tolu's https://github.com/ToluVictor/canvas-apps-tools (#125)
1 parent 9c93064 commit 7a268b8

7 files changed

Lines changed: 255 additions & 6 deletions

File tree

plugins/canvas-apps/.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "canvas-apps",
3-
"version": "1.0.5",
3+
"version": "1.0.6",
44
"description": "Build Power Apps Canvas Apps using the Canvas Authoring MCP server.",
55
"author": {
66
"name": "Microsoft",

plugins/canvas-apps/agents/canvas-app-planner.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ Based on discovery, the user preferences passed in the prompt, and the user's re
8383
- Which controls will drive each screen's layout
8484
- What aesthetic direction fits the app's purpose
8585
- How data will flow (data sources, collections, or mock data)
86-
- **Layout strategy** — default to **AutoLayout** (responsive) using `GroupContainer` with `Variant: Horizontal` or `Variant: Vertical`. Only use ManualLayout if the user explicitly requests pixel-perfect positioning or the app is a fixed-size desktop dashboard. Mobile and cross-device apps MUST use AutoLayout.
86+
- **Layout strategy** — default to **AutoLayout** (responsive) using `GroupContainer` with `Variant: AutoLayout` and `LayoutDirection: =LayoutDirection.Horizontal` or `=LayoutDirection.Vertical`, or if a grid-based layout is appropriate, `Variant: GridLayout`. Only use `Variant: ManualLayout` if the user explicitly requests pixel-perfect positioning or the app is a fixed-size desktop dashboard. Mobile and cross-device apps MUST use AutoLayout.
8787

8888
Enter plan mode (`EnterPlanMode`) and present the following to the user:
8989

plugins/canvas-apps/agents/canvas-screen-builder.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ color: green
99
tools:
1010
- Read
1111
- Write
12+
- Edit
1213
- TaskCreate
1314
- TaskUpdate
1415
---
@@ -61,13 +62,27 @@ Follow the conventions from the plan document's TechnicalGuide Key Conventions s
6162
Write the simplest working version of each formula. The compiler will catch syntax errors —
6263
reserve your reasoning for logic correctness that the compiler cannot catch.
6364

65+
## Step 3.5 — Self-QA
66+
67+
After writing the file, run the runtime-anti-pattern checks that `compile_canvas` does not
68+
catch.
69+
70+
1. Read `${CLAUDE_PLUGIN_ROOT}/references/QAChecks.md`
71+
2. Re-read the `.pa.yaml` file you just wrote
72+
3. Apply each check in order; for every issue found, fix it inline using `Edit`
73+
4. Track the count and a one-line description of every fix applied
74+
75+
Do NOT call `compile_canvas` here — the orchestrating skill owns compilation.
76+
6477
## Step 4 — Return Result
6578

6679
Mark the task complete. Return a concise result to the orchestrating skill:
6780

6881
```
6982
Screen: [Screen Name]
7083
File: [working directory]/[ScreenName].pa.yaml
84+
QA fixes applied: [N]
85+
- [one-line description per fix, or "clean" if N=0]
7186
Status: Written
7287
```
7388

plugins/canvas-apps/agents/canvas-screen-editor.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,24 @@ specification exactly:
7777
Write the simplest working version of each formula. The compiler will catch syntax errors —
7878
reserve your reasoning for logic correctness that the compiler cannot catch.
7979

80+
## Step 3.5 — Self-QA
81+
82+
After applying your changes (Modify) or writing the new screen (Add), run the runtime-anti-pattern
83+
checks that `compile_canvas` does not catch.
84+
85+
1. Read `${CLAUDE_PLUGIN_ROOT}/references/QAChecks.md`
86+
2. Re-read the `.pa.yaml` file you just edited or wrote
87+
3. Apply each check in order; for every issue found, fix it inline using `Edit`
88+
4. Track the count and a one-line description of every fix applied
89+
90+
**Scope for Modify actions:** focus QA checks on controls and containers you changed or added.
91+
Do not rewrite pre-existing issues that are unrelated to this edit — the user did not ask for
92+
them. If a check matches a control you did not touch, skip it.
93+
94+
**Scope for Add actions:** apply all checks to the full new screen, same as the builder.
95+
96+
Do NOT call `compile_canvas` here — the orchestrating skill owns compilation.
97+
8098
## Step 4 — Return Result
8199

82100
Mark the task complete. Return a concise result to the orchestrating skill:
@@ -85,6 +103,8 @@ Mark the task complete. Return a concise result to the orchestrating skill:
85103
Screen: [Screen Name]
86104
Action: [Modify / Add]
87105
File: [working directory]/[ScreenName].pa.yaml
106+
QA fixes applied: [N]
107+
- [one-line description per fix, or "clean" if N=0]
88108
Status: Done
89109
Changes applied: [brief list of what was changed/added]
90110
```
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
# Canvas App YAML — QA Self-Check Guide
2+
3+
This guide lists runtime layout issues that `compile_canvas` does NOT catch. The
4+
compiler validates syntax and property names. It cannot tell you that your
5+
scrollable container will never scroll, or that a transparent overlay button
6+
will collapse its siblings to zero height.
7+
8+
Agents that write `.pa.yaml` files MUST run these checks against their own
9+
output before returning, and fix every issue inline. Report the total number of
10+
fixes applied in the result summary.
11+
12+
---
13+
14+
## How to run the checks
15+
16+
1. Read the `.pa.yaml` file you just wrote
17+
2. Apply each check below in order
18+
3. For every issue found: apply the fix directly using `Edit`
19+
4. Track the count and a one-line description of each fix
20+
5. Do NOT re-run `compile_canvas` here — the orchestrating skill does that
21+
22+
All checks are safe: they tighten existing YAML, never delete semantic content.
23+
24+
---
25+
26+
## Check 1 — LayoutMinWidth / LayoutMinHeight on every GroupContainer
27+
28+
**Problem:** Power Apps defaults `LayoutMinWidth` to 250 and `LayoutMinHeight` to
29+
100 on `GroupContainer`. In a sidebar, header, or narrow cell, these defaults
30+
silently push the container wider/taller than intended and clip siblings.
31+
32+
**Detect:** For every control with `Control: GroupContainer`, check whether
33+
`LayoutMinWidth: =0` and `LayoutMinHeight: =0` are present in `Properties:`.
34+
35+
**Fix:** Add either property if missing:
36+
37+
```yaml
38+
LayoutMinWidth: =0
39+
LayoutMinHeight: =0
40+
```
41+
42+
**Exception:** None. Always set both on every GroupContainer.
43+
44+
---
45+
46+
## Check 2 — AlignInContainer on every AutoLayout child
47+
48+
**Problem:** Children of an AutoLayout container (any container that sets
49+
`LayoutDirection`) have unpredictable cross-axis alignment when
50+
`AlignInContainer` is omitted. PA picks a default that depends on control type.
51+
52+
**Detect:** For every control, check whether its parent has a `LayoutDirection`
53+
property. If yes, check whether the child has `AlignInContainer` set. If not,
54+
it's missing.
55+
56+
**Fix:** Add `AlignInContainer: =AlignInContainer.Stretch` to the child. This
57+
is the correct default for labels, inputs, buttons, and generic content — the
58+
child fills the parent's cross-axis dimension.
59+
60+
```yaml
61+
AlignInContainer: =AlignInContainer.Stretch
62+
```
63+
64+
**Exception:** If the child has an explicit smaller-than-parent cross-axis
65+
dimension (e.g., a 28px circular avatar inside a 44px horizontal row), use
66+
`AlignInContainer: =AlignInContainer.Center` instead, so the child keeps its
67+
natural size and is centered.
68+
69+
---
70+
71+
## Check 3 — SCROLL-TRAP (`FillPortions: =1` inside scroll container)
72+
73+
**Problem:** When a container has `LayoutOverflowY: =LayoutOverflow.Scroll` and
74+
its direct child has `FillPortions: =1`, the child is pinned to the viewport
75+
height. Content that exceeds the viewport is clipped, not scrolled — the whole
76+
point of the scroll container is defeated.
77+
78+
**Detect:** For every container with `LayoutOverflowY: =LayoutOverflow.Scroll`
79+
inspect its direct children. Flag any direct child that has `FillPortions: =1`.
80+
81+
**Fix:** Change the child's `FillPortions` to `=0`.
82+
83+
```yaml
84+
# Before:
85+
FillPortions: =1
86+
# After:
87+
FillPortions: =0
88+
```
89+
90+
---
91+
92+
## Check 4 — WRAP-MISSING (single-line label without `Wrap: =false`)
93+
94+
**Problem:** Power Apps defaults `Wrap` to `true` on `Label` controls. A narrow
95+
nav item, breadcrumb, badge, or KPI value will wrap its text onto two lines and
96+
break the intended layout.
97+
98+
**Detect:** For every `Label` (including `ModernText`), check whether
99+
`Wrap: =false` is set. Flag any label that looks like a single-line UI element:
100+
101+
- Nav/menu item labels (inside a navigation gallery or sidebar)
102+
- Tab labels
103+
- Logo text labels
104+
- Column headers in tables or galleries
105+
- Status badges / pill text
106+
- KPI metric values and card titles
107+
- Breadcrumb text
108+
- Button-adjacent short descriptors
109+
110+
**Fix:** Add `Wrap: =false` to the label's Properties.
111+
112+
```yaml
113+
Wrap: =false
114+
```
115+
116+
**Exception:** Do NOT add `Wrap: =false` to labels that intentionally display
117+
multi-line content — description paragraphs, body copy, notes fields, long
118+
comment text. These should keep the default wrapping behavior.
119+
120+
---
121+
122+
## Check 5 — NO-HEIGHT-TRAP (`FillPortions: =0` without explicit `Height`)
123+
124+
**Problem:** When an AutoLayout child has `FillPortions: =0` (or `FillPortions`
125+
is absent, which defaults to 0) and no explicit `Height`, Power Apps defaults
126+
its height to 200px. This pushes surrounding controls around and produces
127+
inexplicable gaps or clipping.
128+
129+
**Detect:** For every `GroupContainer` whose parent has `LayoutDirection`
130+
(AutoLayout child), check:
131+
- Is `FillPortions` absent or `=0`?
132+
- Is `Height` absent?
133+
- If both → flag it.
134+
135+
**Fix:** Add an explicit `Height` formula that sums child heights + gaps +
136+
padding:
137+
138+
```yaml
139+
Height: =PaddingTop + child1.Height + LayoutGap + child2.Height + PaddingBottom
140+
```
141+
142+
If the children's heights are unknown at write time, use a safe static value
143+
(e.g., `Height: =44` for a single row, `=200` for a card panel) and note it in
144+
the fix log so the user can refine.
145+
146+
**Exception:** The screen root container uses `Width: =Parent.Width` and
147+
`Height: =Parent.Height` — not an AutoLayout child. Do NOT flag it.
148+
149+
Also do NOT flag controls where `FillPortions > 0` — PA computes the height
150+
proportionally and `Height` should be absent.
151+
152+
---
153+
154+
## Check 6 — TEXT-PADDING (ModernText, Label padding defaults to 5)
155+
156+
**Problem:** `ModernText` and `Label` controls default `PaddingTop`, `PaddingBottom`,
157+
`PaddingLeft`, and `PaddingRight` to `5`. In most UI contexts (labels in a
158+
table row, card header text, inline metadata, KPI values), the 5px default is
159+
unintended and breaks alignment with adjacent controls or adds stray visual
160+
space in tight layouts.
161+
162+
**Detect:** For every control with `Control: ModernText` or `Control: Label`, check whether all
163+
four padding properties — `PaddingTop`, `PaddingBottom`, `PaddingLeft`, and
164+
`PaddingRight` — are explicitly set in `Properties:`. Flag any that are
165+
absent.
166+
167+
**Fix:** For each of the four properties that is absent, add it with value
168+
`=0`:
169+
170+
```yaml
171+
PaddingTop: =0
172+
PaddingBottom: =0
173+
PaddingLeft: =0
174+
PaddingRight: =0
175+
```
176+
177+
**Exception:** If the design explicitly requires internal padding on a
178+
`ModernText` (e.g., a status pill or badge where inset text is intended), set
179+
the intended non-zero value explicitly. The rule is **never leave any of the
180+
four padding properties absent on a `ModernText`** — always set all four so the
181+
PA default of 5 cannot creep in.
182+
183+
---
184+
185+
## Check 7 — FILLPORTIONS-HEIGHT-CONFLICT (both set on the same control)
186+
187+
**Problem:** Setting both `FillPortions: =N` (where `N > 0`) and an explicit
188+
`Height: =value` on the same control within a vertical AutoLayout container confuses the layout engine.
189+
The container renders one size at design time and another at runtime.
190+
191+
**Detect:** For every control, check whether it has both:
192+
- `FillPortions` with a value greater than 0, AND
193+
- An explicit `Height` (any non-formula numeric or a formula that doesn't
194+
reference Parent)
195+
196+
**Fix:** Remove the `Height` property. PA computes it from `FillPortions`
197+
against the parent's available space.
198+
199+
---
200+
201+
## Check 8 — FILLPORTIONS-WIDTH-CONFLICT (both set on the same control)
202+
203+
**Problem:** Setting both `FillPortions: =N` (where `N > 0`) and an explicit
204+
`Width: =value` on the same control within a horizontal AutoLayout container
205+
confuses the layout engine. The container renders one size at design time and
206+
another at runtime.
207+
208+
**Detect:** For every control, check whether it has both:
209+
- `FillPortions` with a value greater than 0, AND
210+
- An explicit `Width` (any non-formula numeric or a formula that doesn't
211+
reference Parent)
212+
213+
**Fix:** Remove the `Width` property. PA computes it from `FillPortions`
214+
against the parent's available space.
215+
216+
---

plugins/canvas-apps/references/TechnicalGuide.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ This applies anywhere a record literal appears inline: `Default`, `Selected`, `I
9595
| Use Case | Control Type | Variant |
9696
|----------|--------------|---------|
9797
| Precise positioning | `GroupContainer` | `ManualLayout` |
98-
| Horizontal Responsive layout | `GroupContainer` | `Horizontal` with `LayoutDirection: =LayoutDirection.Horizontal` |
99-
| Vertical Responsive layout | `GroupContainer` | `Vertical` with `LayoutDirection: =LayoutDirection.Vertical` |
98+
| Horizontal Responsive layout | `GroupContainer` | `AutoLayout` with `LayoutDirection: =LayoutDirection.Horizontal` |
99+
| Vertical Responsive layout | `GroupContainer` | `AutoLayout` with `LayoutDirection: =LayoutDirection.Vertical` |
100100

101101
⚠️ **`GroupContainer` has no `OnSelect` — it cannot be clicked.** This is a common dead end when building card UI: the container lays out perfectly but tapping it does nothing.
102102

plugins/canvas-apps/skills/generate-canvas-app/SKILL.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ ones that need answers):
7474
- **Phone users** → large touch targets, single-column, simplified navigation, AutoLayout (responsive), minimal typing.
7575
- **Multi-device / unknown** → AutoLayout (responsive) required.
7676

77-
**Layout strategy rule of thumb:** Default to **AutoLayout** (responsive) unless the user explicitly chose desktop-only or requested pixel-perfect positioning. AutoLayout uses `GroupContainer` with `Variant: Horizontal` or `Variant: Vertical` and `LayoutDirection`, which adapts to different screen sizes. ManualLayout uses fixed `X`/`Y`/`Width`/`Height` values that do not reflow.
78-
7977
---
8078

8179
## Phase 2 — Plan

0 commit comments

Comments
 (0)