Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1dbc595
Setup-solution skill verified for 100 components site
tyaginidhi Mar 11, 2026
487383c
Fix export-solution bugs found during live testing against IdeaSphere
tyaginidhi Mar 12, 2026
66017e5
Fix import-solution bugs found during live testing against IdeaSphere
tyaginidhi Mar 12, 2026
0f51b9e
Add AttachmentBlocked import error pattern; fix import SKILL.md case bug
tyaginidhi Mar 12, 2026
fcd472f
Add Phase 5b: resolve AttachmentBlocked errors in import-solution
tyaginidhi Mar 12, 2026
59986ca
Clarify Phase 5b consent: invoke AskUserQuestion directly, not as cha…
tyaginidhi Mar 12, 2026
b07ef1d
export-solution: ask managed vs unmanaged via AskUserQuestion directly
tyaginidhi Mar 12, 2026
5408b88
Remove er-diagram-ideasphere.png — not part of skill code
tyaginidhi Mar 12, 2026
5e316f1
power pages alm using solutions (#47)
tyaginidhi Mar 13, 2026
7472b0b
Add setup-pipeline and deploy-pipeline skills; remove generate-pipeli…
tyaginidhi Mar 31, 2026
0f42f77
Merge origin/main into users/nityagi/PowerPagesALM
tyaginidhi Mar 31, 2026
791c24b
Deployment variables, CI/CD pipeline deploy, and setup-solution impro…
tyaginidhi Apr 1, 2026
5074049
Merge remote-tracking branch 'origin/main' into users/nityagi/PowerPa…
tyaginidhi Apr 1, 2026
2b05b6b
import-solution + deploy-pipeline: env var prompting and post-deploy …
tyaginidhi Apr 2, 2026
8d5f85c
deploy-pipeline: tabbed deploy history + solution contents; plan-alm:…
tyaginidhi Apr 13, 2026
05317a5
Add solution-splitting decision tree and manifest v2 (#107)
tyaginidhi Apr 20, 2026
fe7a431
Merge remote-tracking branch 'origin/main' into users/nityagi/PowerPa…
tyaginidhi Apr 20, 2026
973adec
Add ALM-aware-by-default safeguards for Power Pages skills (#108)
tyaginidhi Apr 20, 2026
4ea10b7
ALM validation run: 11 fixes + integration tests + live-verified end-…
tyaginidhi Apr 23, 2026
67a16f9
Merge branch 'main' into users/nityagi/PowerPagesALM
tyaginidhi Apr 24, 2026
4a128f7
Power Pages ALM: per-skill telemetry on 6 skills + 5 plan-alm UX fixe…
tyaginidhi Apr 27, 2026
9bb2a3a
ensure-pipelines-host skill + plan-alm UX overhaul + 3-entity site mo…
tyaginidhi Apr 29, 2026
d888303
Merge remote-tracking branch 'origin/main' into users/nityagi/PowerPa…
tyaginidhi Apr 29, 2026
cb9b0f2
ALM plan UX overhaul: env-first host menu, post-run refresh, SKU fall…
tyaginidhi May 5, 2026
b6ad412
plan-alm + ensure-pipelines-host: cap eligible-env list at 5 in env-f…
tyaginidhi May 6, 2026
5fff6d7
ensure-pipelines-host: ship Platform Host getOrCreate as Phase 4.0 (#…
tyaginidhi May 7, 2026
53895d2
Merge remote-tracking branch 'origin/main' into users/nityagi/PowerPa…
tyaginidhi May 7, 2026
afb55ad
ALM skills release-readiness: plan-UI fixes, OAuth handling, MCP grou…
tyaginidhi May 8, 2026
3efde0c
force-link-environment skill + setup-pipeline Pattern 15 auto-fix (#149)
tyaginidhi May 11, 2026
17d92cb
ALM skills: artifacts under docs/alm, post-sync approval gates, estim…
tyaginidhi May 14, 2026
56b2cac
Merge remote-tracking branch 'origin/main' into featureBranch/PowerPa…
tyaginidhi May 14, 2026
f5aefce
ALM split + plan-state auto-sync + getOrCreate + pre-flight unblock (…
tyaginidhi May 17, 2026
17b5ef5
Approval Gates: catalog, marker sweep + lint, loop-iteration tighteni…
tyaginidhi May 21, 2026
4949d99
Merge remote-tracking branch 'origin/main' into featureBranch/PowerPa…
tyaginidhi May 22, 2026
990ab40
Env var lifecycle: pre-PATCH validate + post-deploy verify + canonica…
tyaginidhi May 22, 2026
84bae64
Manual export/import reliability + parallel validation batch (#164)
tyaginidhi May 25, 2026
bf95c03
Merge remote-tracking branch 'origin/main' into featureBranch/PowerPa…
tyaginidhi May 25, 2026
b0cda13
ALM review follow-ups: B1-B4 + D1, heartbeat Phase 0, halt-on-deploy-…
tyaginidhi May 25, 2026
e3b76ef
Auto-add plugin version check to SKILL.md files
github-actions[bot] May 25, 2026
31073e9
Bump power-pages plugin version 1.6.0 → 2.0.0 for main-bound PR #167
tyaginidhi May 25, 2026
13a0d74
AGENTS.md: fix three doc-drift items flagged in pre-merge review
tyaginidhi May 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/power-pages-alm-lint.yml
Comment thread
tyaginidhi marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: power-pages-alm-lint

on:
pull_request:
branches:
- main
paths:
- "plugins/power-pages/**"

jobs:
alm-lint:
name: alm-lint
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4

- name: setup-node
uses: actions/setup-node@v4
with:
node-version: 20

- name: run-alm-lint
# Fails the check when any SKILL.md, script, or new powerpagecomponenttype
# violates the ALM-aware-by-default rules documented in
# plugins/power-pages/AGENTS.md. See PLUGIN_DEVELOPMENT_GUIDE.md for the
# full checklist and the allowlist mechanism (.almlintignore / inline
# `alm-lint-ignore:` comments).
run: node plugins/power-pages/scripts/lint-skills-alm.js
17 changes: 14 additions & 3 deletions plugins/power-pages/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "power-pages",
"version": "1.3.0",
"description": "Create and deploy Power Pages sites using modern development approaches. Supports code sites (SPAs) with React, Angular, Vue, or Astro, with more site types coming soon.",
"version": "2.0.0",
"description": "Create and deploy Power Pages sites using modern development approaches. Supports code sites (SPAs) with React, Angular, Vue, or Astro. Includes ALM orchestration (plan-alm) with a solution-splitting decision tree, per-solution pipelines, Azure Blob asset advisory, manifest schema v2 for multi-solution deployments, and force-link remediation for cross-host pipeline migrations.",
"author": {
"name": "Microsoft",
"url": "https://www.microsoft.com"
Expand All @@ -23,6 +23,17 @@
"odata",
"web-api",
"tables",
"schema"
"schema",
"solution",
"alm",
"plan-alm",
"ci-cd",
"pipeline",
"pipelines-host",
"force-link",
"diagnostics",
"shared-lib",
"solution-splitting",
"asset-advisory"
]
}
313 changes: 310 additions & 3 deletions plugins/power-pages/AGENTS.md

Large diffs are not rendered by default.

148 changes: 148 additions & 0 deletions plugins/power-pages/PLUGIN_DEVELOPMENT_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ Every skill pauses for user approval at three junctures:

Between checkpoints, skills work **autonomously** — no mid-analysis questions.

> **Approval Gates — canonical catalog.** Every individual `AskUserQuestion` that meets the gate test (would Cancel leave partial or complete-but-wrong state behind?) is an **Approval Gate**. See `references/approval-gates.md` for the canonical terminology, the six categories (`intent` / `plan` / `progress` / `consent` / `final` / `pause`), the marker syntax (`<!-- gate: skill:phase | category=X | cancel-leaves=Y -->` + human-readable `> 🚦 **Gate (...)**` block), the per-skill catalog, and the lint rules (`GATE-must-have-marker`, `GATE-id-must-be-unique`, `GATE-must-be-in-catalog`, `GATE-intent-must-call-helper`, `GATE-cancel-leaves-known-vocab`). ALM skills enforce these rules with `severity: 'error'`; non-ALM skills currently warn-only until the catalog extends.

### Approval in Practice

| Skill | Checkpoint 1 (Discovery) | Checkpoint 2 (Plan) | Checkpoint 3 (Deploy) |
Expand Down Expand Up @@ -302,3 +304,149 @@ their own contacts and create new ones, but cannot modify or delete existing rec
> **Acceptance criterion:** Every skill must implement the three-point approval pattern. No approval-gated action may proceed without explicit user confirmation via `AskUserQuestion`. Skills must work autonomously between checkpoints — no mid-analysis questions.

---

## ALM Checklist for New Skills

Any skill that creates, modifies, or depends on Dataverse records that belong in a Power Pages site's solution (site components, env var definitions, web roles, site settings, server logic, cloud flow bindings, bot consumers, custom tables, etc.) **must** comply with the ALM-aware-by-default principle documented in `AGENTS.md`. Concretely, before merging:

- [ ] **SKILL.md Phase 1** reads `.solution-manifest.json` if present; stores `solution.uniqueName` for downstream phases
- [ ] Any `scripts/*.js` that writes to Dataverse accepts a `--solutionUniqueName` argument and imports `./lib/resolve-target-solution` to honor the [strict resolution order](AGENTS.md#alm-aware-by-default)
- [ ] Records created by the skill are added to the resolved solution via `AddSolutionComponent` (never silently left in `Default`)
- [ ] Any new `powerpagecomponenttype` values used in the skill are reflected in `scripts/lib/discover-site-components.js` (`PPC_TYPE_LABELS`). Discovery must never skip a type
- [ ] Skills that create Dataverse artifacts but might not know the target solution (e.g. utility skills, skills that can run before `setup-solution`) end by prompting the user to run `/power-pages:setup-solution` in sync mode
- [ ] `node scripts/lint-skills-alm.js` reports **zero findings** on the changed skill + scripts
- [ ] A `node:test` suite covers the new component-creation script, including an assertion that `--solutionUniqueName` flows through to `AddSolutionComponent`

### Solution Resolution Order

When a skill or script needs "which solution?", resolve in this order and stop at the first match:

1. **Explicit `--solutionUniqueName` CLI argument / skill argument** — always wins
2. **`.solution-manifest.json` in project root** — the default path
3. **Neither present** — interactive skill: prompt via `AskUserQuestion` with a list of candidate user solutions (publisher-prefix matches) + option to run `/power-pages:setup-solution` first. Non-interactive script: exit with `NoSolutionConfiguredError` and a clear hint. **Never silently fall back to `Default`.**

### Lint Command

Run locally before submitting a PR:

```powershell
node plugins/power-pages/scripts/lint-skills-alm.js
```

Exits 0 with `alm-lint: 0 findings` when clean; exits 1 and prints file/rule/message for each violation otherwise. Waive individual findings with an `alm-lint-ignore: <rule-name> — <short reason>` comment at the relevant line (prefer `<!-- … -->` in Markdown, `// …` in JS).

### Related Helpers

- `scripts/lib/resolve-target-solution.js` — implements the 3-step resolution order; use from every component-creation script
- `scripts/lib/discover-site-components.js` — one-call site inventory (powerpagecomponents, flows, env vars, custom tables) + diff against an existing solution; use in Inventory / pre-export phases

> **Acceptance criterion:** No component-creation skill may ship that leaves a Dataverse record orphaned in `Default`. The lint, the resolver, and the discovery module together make this the path of least resistance — please use them.

---

## Hook design for skill validation

Hook validators run after a skill executes (or, badly, every time the assistant pauses) to surface incomplete state to the agent. **Get this wrong and you cost users real money.** A real incident on this plugin (BYOC supplier portal, 2026-05-04) had three Stop hooks LLM-evaluating skill-completion every turn, all returning `{ ok: false }` with multi-paragraph reasons because the user had explicitly deferred ALM. The agent kept acknowledging, the hooks kept refiring on each acknowledgement, and the transcript grew quadratically until the cost was visible.

### Anti-patterns — do not use these

| Pattern | Why it's wrong |
|---|---|
| **`type: prompt` Stop hooks for skill-completion** | Stop fires on every assistant pause, including user-input waits. LLM-evaluation can't reliably tell "this skill wasn't supposed to run" from "this skill failed", so it returns `ok: false` whenever artifacts are missing — forcing continuation. Combine with multiple skills' hooks all firing per turn, and you have a runaway loop. The plugin removed all of these in commit `e670581`. **Do not re-introduce.** |
| **`process.exit(2)` (block) for soft "did this complete?" checks** | Blocking exit forces continuation. For "did the skill complete cleanly?" you almost never want forced continuation — you want a one-time advisory the agent acknowledges and moves on. Reserve `block()` for **hard correctness gates** only: malformed marker files, `docs/alm/last-deploy.json.status === "Failed"`, lint failures, secrets in diffs. |
| **Re-deriving completion from ephemeral artifacts** | If a validator checks `docs/foo.html` exists and the user legitimately deletes that file (cleanup, project move), the hook fails forever. Validation should reflect *intent*, not *artifact presence*. Use marker files the skill writes deliberately. |
| **Stop hooks that duplicate PostToolUse hooks** | If a skill is already validated by `hooks/hooks.json` PostToolUse on the `Skill` tool (which fires once per skill invocation), a Stop hook running the same validator just adds noise — fires too often. Pick one. **Prefer PostToolUse.** |

### Recommended patterns

#### 1. Deterministic command validators with marker-file gates

Each skill writes a marker file at completion (`docs/alm/last-pipeline.json`, `docs/alm/last-deploy.json`, `docs/alm-plan.html`, `.solution-manifest.json`, etc.). The validator:

```js
const { runValidation, findProjectRoot, approve, block, readDeferralMarker } = require('../../../scripts/lib/validation-helpers');

runValidation((cwd) => {
const projectRoot = findProjectRoot(cwd) || cwd;

// 1. Honor explicit deferral first — silent-approve regardless of state.
if (readDeferralMarker(projectRoot)) return approve();

// 2. No marker -> not a foo session -> silent-approve.
const markerPath = path.join(projectRoot, '.last-foo.json');
if (!fs.existsSync(markerPath)) return approve();

// 3. Marker exists -> validate its shape. Block ONLY on hard failures.
let marker;
try { marker = JSON.parse(fs.readFileSync(markerPath, 'utf8')); }
catch { return block('.last-foo.json could not be parsed as JSON.'); }

if (marker.status === 'Failed') {
return block('Last foo run failed (id: ' + marker.runId + '). Investigate before retrying.');
}
if (!marker.requiredField) {
return block('.last-foo.json is missing required field: requiredField');
}

return approve();
});
```

This pattern doesn't loop: silent-approve produces no output, no forced continuation. Block fires only when the marker exists AND is genuinely broken.

#### 2. Honor deferral markers before any other check

If a user explicitly defers a skill family (e.g. ALM for a project handled by infra team's pipeline), they drop a marker file (`.alm-deferred` for ALM). All related validators short-circuit on this marker as their FIRST check.

`scripts/lib/validation-helpers.js` exports `readDeferralMarker(projectRoot)` — every ALM validator on this plugin calls it first. The marker is recognized in three formats: empty (touch file), plain text (one-line reason), or JSON (`{ deferredAt, deferredBy, reason, scope }`).

User-facing usage:
```bash
# At the project root:
echo '{"reason":"ni-dev — ALM handled by infra"}' > .alm-deferred
```

#### 3. Prefer PostToolUse over Stop for skill-completion

PostToolUse on the `Skill` tool fires **once per skill invocation**. Stop fires on **every assistant pause** (including user-input waits — every "Continue?" prompt fires it).

This plugin uses PostToolUse via `hooks/hooks.json` → `run-skill-posttool-validation.js` → per-skill validator. Skill frontmatter must NOT declare its own `hooks: Stop:` block — those duplicate the centralized PostToolUse hook and fire too often. To wire validation for a new skill, register it in the `TRACKED_SKILLS` map in `scripts/lib/powerpages-hook-utils.js` (see `AGENTS.md` → "Hooks" for the registration steps).

#### 4. Skills write explicit status, not just artifact presence

Marker files include a `status` field:

```json
{
"status": "Completed", // or "Draft" | "Approved" | "In Execution" | "Deferred" | "Failed"
...
}
```

Validators check `status` when present rather than re-deriving completion from secondary artifacts. A `"Deferred"` status in the marker file is also a valid signal — silent-approve regardless of other field presence.

### When you genuinely need a hard gate

Some checks DO warrant blocking — they're not "did the skill complete?" checks but "did something go wrong that requires the agent to retry?" Examples that justify `block()`:

- Marker file exists but is malformed JSON.
- Marker file's `status === "Failed"` and the agent should investigate before continuing.
- Required field is missing from a present marker (e.g. `docs/alm/last-pipeline.json` without `pipelineId`).
- Lint failure on a file the skill just wrote.
- Secrets detected in a diff the skill is about to commit.

These all share a property: the artifact is present, not absent. Absent artifacts always silent-approve.

### Acceptance criterion

A new skill's validator must:

1. Silent-approve when its marker file is absent.
2. Silent-approve when `.alm-deferred` (or skill-specific deferral marker) is present.
3. Block ONLY when the marker is present AND in a state that requires retry.
4. Not register a `type: prompt` Stop hook under any circumstances.
5. Use `runValidation()` from `validation-helpers.js` so it inherits the standard try/catch and silent-approve-on-error fallback.

If your validator can return `block()` when no skill-related work happened in the session, fix it before merging. The cost of getting this wrong is real-money runaway loops, not just noisy errors.

---
5 changes: 5 additions & 0 deletions plugins/power-pages/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,13 +290,18 @@ The plugin invokes multiple tools during a session. To reduce approval prompts:
claude --dangerously-skip-permissions
```

## ALM prompts you may see

Several skills now ask about solution identity, orphan components, and pre-export completeness. These prompts catch a real class of bugs where Dataverse records silently stay behind in the `Default` solution. See **[`references/alm-prompts.md`](references/alm-prompts.md)** for a user-facing walkthrough of each prompt and how to respond.

## Documentation

- [Power Pages AI Plugin Documentation](https://learn.microsoft.com/power-pages/configure/create-code-site-using-claude-code)
- [Power Pages Code Sites](https://learn.microsoft.com/power-pages/configure/create-code-sites)
- [PAC CLI Reference](https://learn.microsoft.com/power-platform/developer/cli/reference/pages)
- [Power Pages REST API](https://learn.microsoft.com/rest/api/power-platform/powerpages/websites)
- [Dataverse Web API](https://learn.microsoft.com/power-apps/developer/data-platform/webapi/overview)
- [ALM prompts — user guide](references/alm-prompts.md)

## Testing validator scripts

Expand Down
Loading
Loading