diff --git a/plugins/power-pages/.claude-plugin/plugin.json b/plugins/power-pages/.claude-plugin/plugin.json index 40e5a6a49..89a3893b1 100644 --- a/plugins/power-pages/.claude-plugin/plugin.json +++ b/plugins/power-pages/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "power-pages", - "version": "2.6.3", + "version": "2.7.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", diff --git a/plugins/power-pages/.plugin/plugin.json b/plugins/power-pages/.plugin/plugin.json index 40e5a6a49..89a3893b1 100644 --- a/plugins/power-pages/.plugin/plugin.json +++ b/plugins/power-pages/.plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "power-pages", - "version": "2.6.3", + "version": "2.7.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", diff --git a/plugins/power-pages/references/skill-tracking-reference.md b/plugins/power-pages/references/skill-tracking-reference.md index 994f7038a..0aa43ee9b 100644 --- a/plugins/power-pages/references/skill-tracking-reference.md +++ b/plugins/power-pages/references/skill-tracking-reference.md @@ -42,6 +42,7 @@ If the tracking script creates or updates site setting YAML files, include those | add-cloud-flow | AddCloudFlow | Site/AI/Skills/AddCloudFlow | | add-ai-webapi | AddAiWebapi | Site/AI/Skills/AddAiWebapi | | integrate-backend | IntegrateBackend | Site/AI/Skills/IntegrateBackend | +| migrate-datamodel | MigrateDatamodel | Site/AI/Skills/MigrateDatamodel | | scan-site | ScanSite | Site/AI/Skills/ScanSite | | manage-headers | ManageHeaders | Site/AI/Skills/ManageHeaders | | manage-firewall | ManageFirewall | Site/AI/Skills/ManageFirewall | diff --git a/plugins/power-pages/skills/migrate-datamodel/BugBash-MigrateSdmToEdm.docx b/plugins/power-pages/skills/migrate-datamodel/BugBash-MigrateSdmToEdm.docx new file mode 100644 index 000000000..4be02bff1 Binary files /dev/null and b/plugins/power-pages/skills/migrate-datamodel/BugBash-MigrateSdmToEdm.docx differ diff --git a/plugins/power-pages/skills/migrate-datamodel/DESIGN.md b/plugins/power-pages/skills/migrate-datamodel/DESIGN.md new file mode 100644 index 000000000..e96b49ead --- /dev/null +++ b/plugins/power-pages/skills/migrate-datamodel/DESIGN.md @@ -0,0 +1,456 @@ +# Skill Design: Migrate Site from Standard Data Model (SDM) to Enhanced Data Model (EDM) + +**Status:** Implemented (12-phase plan; preview feature, ALM integration deferred) +**Source Doc:** [Migrate standard data model sites to enhanced data model (preview)](https://learn.microsoft.com/en-us/power-pages/admin/migrate-enhanced-data-model) +**Plugin:** power-pages + +--- + +## Overview + +The **Enhanced Data Model (EDM)** is the next-generation storage model for Power Pages sites. Instead of spreading site configuration across many bespoke `adx_*` Dataverse tables (the legacy **Standard Data Model**, or **SDM**), EDM consolidates site metadata into a small set of unified tables — most notably `powerpagecomponent` — where component-specific properties are stored as JSON in a `content` column. The result is a simpler, future-proof schema that the Power Pages platform and tooling can evolve without per-component table churn, cleaner ALM with fewer tables to package, faster runtime resolution because the platform no longer joins across many `adx_*` tables, and a consistent surface for new Power Pages features that are being built EDM-first. + +It's important to note that **not all `adx_*` tables move into `powerpagecomponent`**. Only the **metadata** `adx_*` tables — the ones that describe the structure and authoring surface of the site, such as `adx_webpage`, `adx_webtemplate`, `adx_contentsnippet`, `adx_sitesetting`, `adx_pagetemplate`, `adx_weblink`, `adx_entityform`, `adx_entitylist`, and similar configuration tables — are consolidated into `powerpagecomponent` (with their per-row properties moved into the `content` JSON column). The **transactional / runtime** `adx_*` tables — the ones that capture end-user activity at runtime, such as `adx_invitation`, `adx_inviteredemption`, `adx_portalcomment`, `adx_externalidentity`, and the entity-form / advanced-form submission and log tables — are **not** migrated into `powerpagecomponent`; they remain on their existing schemas and keep storing runtime data as before. What changes for those transactional tables is that their lookups to metadata records get rewired during the references migration so they point at the new `powerpagecomponent` rows instead of the legacy metadata `adx_*` rows. + +Existing sites were authored on SDM and continue to run on `adx_*` tables, so to benefit from EDM (and to stay aligned with where the Power Pages platform is headed) each site must be **migrated**. Migration moves the site's configuration metadata into the EDM `powerpagecomponent` shape, rewires transactional references onto those new metadata records, and flips the site record to serve from EDM. Migration is also where **customizations** — custom `adx_*` columns, Liquid that reads `adx_*` attributes, FetchXML over `adx_*` tables, plugins, and workflows — are surfaced and **remediated**, because those customizations don't carry over automatically and must be rewritten or restructured to work against EDM. + +The skill is structured as **4 high-level phases**. Phase 1 and Phase 4 run the same way for every site, while Phase 2 and Phase 3 are **track-branched** — their shape depends on the migration mode chosen in step 1.7, which derives the track from the environment type. The **Authoring Track** (mode `configurationData` or `all`) is used for Dev and Single-environment setups, where the metadata itself is migrated locally and customizations are scanned and fixed against SDM source before references move. The **Downstream Track** (mode `configurationDataReferences`) is used for Test, UAT, and Production environments where configuration metadata is assumed to have already arrived via ALM solution import from Dev; only transactional references migrate here, and any customization findings indicate an upstream ALM gap rather than work the user should do locally. + +**Phase 1 — Site Discovery & Pre-checks** runs for both tracks and covers seven sub-steps: establish CLI context (1.1), identify the site context (1.2), discover the site and validate it's on SDM (1.3), check for any prior or in-flight migration (1.4), validate the required Dataverse dependencies (1.5), validate the template and its V2 EDM package (1.6), and determine the environment type plus migration mode (1.7) — this last sub-step is where the track is derived. + +On the **Authoring Track** (17 sub-steps total), **Phase 2 — Configuration Migration & Customization Remediation** captures an SDM baseline snapshot (2.1), migrates the configuration metadata with `pac pages migrate-datamodel --mode ` (2.2), locates the auto-emitted `SiteCustomization*.csv` report (2.3), and remediates customizations by staging FetchXML and Liquid auto-rewrites alongside augmented prompts for plugins and Data Model Extensions, then applying the staged diff and uploading back to Dataverse with `pac pages upload --modelVersion 1` (2.4). **Phase 3 — Migration Execution** then runs a four-sub-step path: an SDM↔EDM data diff validation as a pre-refs safety gate (3.1), the transactional references migration (3.2, auto-skipped when mode was `all`), EDM activation via `--updateDataModelVersion --portalId <…>` (3.3), and the user-confirmed site restart (3.4). + +On the **Downstream Track** (18 sub-steps total), **Phase 2 — Setting Up Metadata** is shorter because configuration metadata is assumed to have arrived via ALM: verify the metadata is present in the target environment (2.1), capture snapshots for later diffs (2.2), and confirm metadata readiness via a user-facing gate before Phase 3 starts moving transactional data (2.3). **Phase 3 — Migration Execution** is longer here because customizations are scanned and remediated after the refs migration emits its own customization report: data diff validation (3.1), migrate refs with `--mode configurationDataReferences` (3.2), locate the customization report (3.3), remediate customizations with the same staged-rewrite and augmented-prompt flow as the Authoring Track but with a stronger warning since Prod/Test/UAT findings typically signal an ALM gap upstream (3.4), activate EDM (3.5), and confirm the site restart (3.6). + +**Phase 4 — Post-Migration Validation** is also shared across tracks and consists of two sub-steps: a runtime smoke-test recommendation that points the user at the `/test-site` flow (4.1) and a final status summary that records skill usage and writes the final execution report (4.2). + +> **Note:** EDM migration is a preview feature. Behavior may change before GA. Always test on a non-production environment first. + +--- + +## Scope + +### In Scope + +- PAC CLI version and authentication validation +- Site discovery via `pac pages list -v` (Portal Id auto-captured from verbose output) +- **In-flight migration detection** (step 1.4) — handles already-running, completed, failed, and reverted prior migrations +- Dependency and template-package validation +- Environment-aware migration mode recommendation (Dev → `all`, Test/UAT/Prod → `configurationData`) +- Customization report generation (always, for any environment) +- Pre-migration customization remediation (manual guidance + automated fixes for Data Model Extensions) +- Migration execution with bounded polling (30-min ceiling + wait/reset/exit escape hatch) +- Data model version flip via Portal Id (captured from `pac pages list -v` when available; manual `/_services/about` fallback otherwise) +- Rollback to SDM +- HTML report generation (customization report + execution report) + +### Out of Scope + +- Creating new EDM sites from scratch (use a `create-site` skill instead) +- ALM/solution deployment of migration artifacts (deferred to a future skill version; env type is captured but not acted on) +- Dataverse schema design changes unrelated to migration +- Environment-copy operations (only available via Power Platform admin center, no PAC CLI API) + +--- + +## Supported Templates + +All Power Pages and D365 portal templates can be migrated, provided the corresponding V2 EDM solution is installed in the target environment. step 1.6 validates installation per template. + +**Power Pages templates:** + +- Starter layout 1–5 +- Application processing +- Blank page +- Program registration +- Schedule and manage meetings +- FAQ +- Event registration + +**D365 portal templates (migratable in recent PAC CLI builds):** + +- Community Portal +- Customer Self-Service Portal +- Employee Self-Service Portal +- Partner Portal + +Each template maps to a specific V2 solution `UniqueName` (see SKILL.md step 1.6 for the full mapping). Older PAC CLI builds may still reject D365 portal templates — confirm with `pac --version`. + +--- + +## Phase Breakdown + +The skill is organized into **4 high-level phases**. Each phase contains numbered sub-steps for granular execution and progress tracking. + +| # | Phase | Description | Sub-steps | +| --- | --- | --- | --- | +| 1 | **Site Discovery & Pre-checks** | Gather all context needed to plan and execute migration safely. CLI context, site identification, prior migration state detection, dependency verification, template validation, migration mode selection. | 1.1 Establish CLI Context · 1.2 Identify Site Context · 1.3 Site Discovery & Validate Data Model · 1.4 Check Existing Migration Status · 1.5 Validate Required Dependencies · 1.6 Validate Template & V2 Package · 1.7 Determine Env Type & Migration Mode | +| 2 | **Customization Remediation** | Identify customizations, apply auto-rewrites where safe (FetchXML + Liquid), surface per-finding guidance for manual fixes, gate the transition to migration. | 2.1 Generate Customization Report · 2.2 Remediate Customizations (download → rewrite → review → upload → readiness gate) | +| 3 | **Migration Execution** | Run the SDM→EDM migration with the selected mode and flip the data model version. | 3.1 Migrate Site Data Model · 3.2 Update Data Model Version | +| 4 | **Post-Migration Validation** | Validate the migrated site, optionally rollback, produce final execution report. | 4.1 Validation, Optional Rollback, and Final Summary | + +### Sub-step → PAC command mapping + +| Sub-step | Key Commands | +| --- | --- | +| 1.1 | `pac --version`, `pac auth list/who` | +| 1.2 | `Test-Path .\website.yml` + AskUserQuestion | +| 1.3 | `pac pages list -v` | +| 1.4 | `pac pages migrate-datamodel --webSiteId <…> --checkMigrationStatus [--verbose]` | +| 1.5 | `pac solution list --includeSystemSolutions` | +| 1.6 | `pac solution list --includeSystemSolutions`, `pac application install --application-name <…>` | +| 1.7 | AskUserQuestion | +| 2.1 | `pac pages migrate-datamodel --webSiteId <…> --siteCustomizationReportPath `, `node generate-migration-reports.js` | +| 2.2 | `pac pages download --webSiteId <…> --path ./mysite`, `node generate-migration-reports.js --automate-fetchxml --automate-liquid`, `pac pages upload --path --modelVersion 1` | +| 3.1 | `pac pages migrate-datamodel --webSiteId <…> --mode `, `--checkMigrationStatus` (polling) | +| 3.2 | `pac pages migrate-datamodel --webSiteId <…> --updateDatamodelVersion --portalId <…>` | +| 4.1 | `pac pages migrate-datamodel --revertToStandardDataModel --portalId` (only if rollback), `node generate-migration-reports.js` (final report) | + +### Why Customization Remediation (Phase 2) sits before Migration (Phase 3) + +The previous design placed remediation after migration. The current design fixes customizations **before** migration for three reasons: + +1. **File-level rewrites are safer on SDM source.** Both Liquid and FetchXML rewrites operate on the downloaded source files (`.html` / `.yml`); editing SDM-source files and re-uploading via `pac pages upload` is well-understood, with `pac pages download` providing a clean rollback point. +2. **Data Model Extensions need a clean target before metadata moves.** Per the migration doc, the correct fix for a custom column on an `adx_*` table is to create a new custom table with a lookup to `powerpagecomponent`. If this happens before step 3.1, the data migration is straightforward; if deferred to post-migration the user has to navigate the new EDM `content` JSON structure. +3. **Plugins/workflows are deferrable.** These are also recommended pre-migration (so EDM has working plugins from day 1) but can be safely deferred — the step 2.2 final-readiness gate makes that choice explicit. + +The Microsoft doc places all fixes post-migration. The skill deviates here intentionally for the reasons above; the deviation is documented in SKILL.md and DESIGN.md. + +Moving remediation to step 2.2 means the migration runs against a cleaner customization surface. + +### Why step 1.4 was added + +The skill needs to be re-entrant. A user may: + +- Trigger a migration outside of this skill, then run the skill later +- Exit the skill mid-migration and re-invoke after hours +- Hit the step 3.1 polling timeout and need to choose wait/reset/exit + +A single early status check in step 1.4 detects any of these and routes the flow appropriately — including short-circuiting to step 3.2 if a prior migration completed but the data-model version wasn't flipped. + +--- + +## Directory Layout + +The skill operates against two resolved paths captured in step 1.2: + +- **``** — directory containing `website.yml` (the site source files) +- **``** — directory for all migration artifacts (CSV, HTML reports, diffs, state files) + +These are kept **separate** so site source stays clean: a user can `git diff` or commit `` without migration artifacts contaminating the change set. + +### Two scenarios + +**Scenario A — cwd is a working directory** (no `website.yml` in cwd; site lives in a subdirectory or will be downloaded): + +```text +cwd/ +├── mysite/ ← site download lands here (step 2.2 if absent) +│ └── site-1---site-k5s85/ ← — actual site root, has website.yml +│ ├── website.yml +│ ├── web-templates/ +│ │ └── migration-check-demo/ +│ │ └── Migration-Demo-check.webtemplate.source.html ← untouched until user approves +│ └── ... +└── migration-reports/ ← — all migration artifacts + ├── SiteCustomization.csv ← PAC writes here + ├── customization-report.html ← script writes here + ├── sdm-to-edm-migration-report.html + ├── remediation-staged/ ← rewriter-proposed files (mirrors layout) + │ └── web-templates/ + │ └── migration-check-demo/ + │ └── Migration-Demo-check.webtemplate.source.html + └── remediation-diff.json ← structured per-file diff manifest +``` + +**Scenario B — cwd IS the site** (`website.yml` directly in cwd): + +```text +parent/ +├── contoso-portal/ ← cwd; = "." +│ ├── website.yml +│ ├── web-templates/ +│ └── ... +└── migration-reports/ ← = "..\migration-reports" + └── (same contents as above) +``` + +The "go one step back" rule keeps the site dir clean for source control. + +### Resolution algorithm (step 1.2) + +```text +Test-Path .\website.yml +├── True → = "." ; = "..\migration-reports" +└── False → look for subdir with website.yml + ├── Exactly one subdir → = .\ ; = ".\migration-reports" + └── Zero or multiple → download to .\mysite\ in step 2.2; + = ".\migration-reports" +``` + +`` and `` resolved once in step 1.2 and used as durable values across all subsequent phases. + +### Output artifacts + +| File | Producer | Location | +| --- | --- | --- | +| `SiteCustomization.csv` (and auto-numbered `SiteCustomization.csv`) | `pac pages migrate-datamodel --siteCustomizationReportPath` | `` (PAC sometimes writes to cwd instead — step 2.1 globs for it) | +| `customization-report.html` | `generate-migration-reports.js` | `` | +| `sdm-to-edm-migration-report.html` | `generate-migration-reports.js` | `` | +| `remediation-staged/` (proposed file copies) | `generate-migration-reports.js --automate-fetchxml --automate-liquid` | `/remediation-staged/` (mirrors `` layout; live source untouched until apply step) | +| `remediation-diff.json` (structured per-file diff manifest) | same script | `` (consumed by live report's Remediation Diff card and by `apply-remediation.js`) | + +### CSV Location-column path handling + +PAC writes paths to its internal scan temp directory in the CSV's `Location` column (e.g., `\\?\C:\Users\...\Temp\\web-templates\X\Y.html`). These paths are unusable as-is because: + +1. The `\\?\` Windows extended-path prefix breaks Node's URL parser (used by Claude Code when rendering markdown file links — the `?` becomes `%3F` in `pathToFileURL`). +2. The temp directory may be cleaned up after the scan; it's not where the user should operate. + +The script's `normalizeLocationPath()` sanitizes at the parse boundary: + +- Strips `\\?\` and `\\?\UNC\` prefixes +- Strips everything up to and including the `*\Temp\\` segment, returning only the relative path within the site (`web-templates\X\Y.html`) + +Downstream consumers (HTML reports, markdown links, log messages) always get clean relative paths that map directly to the user's ``. + +--- + +## Customization Remediation Categories + +The customization report (step 2.1) categorizes findings into five types. step 2.2 applies a mix of auto-rewrites, per-finding categorization, and per-table manual checklists. The rule is: **fix what the official doc shows as a mechanical rewrite; categorize and recommend everything else**. + +| Type | Sub-pattern | step 2.2 action | +| --- | --- | --- | +| **FetchXml contains adx references** | `` | **Auto-rewritten** — rename to `powerpagecomponent`, inject `powerpagecomponenttype` filter | +| **FetchXml contains adx references** | `` | **Auto-rewritten** — same logic, two-pass to avoid nested-filter collision | +| **FetchXml contains adx references** | `` containing adx_* | Flagged for manual review (semantic-change risk) | +| **FetchXml contains adx references** | Unknown adx_* entity (custom table) | Flagged manually (no `powerpagecomponenttype` mapping) | +| **Liquid contains adx references** | `entities['adx_*']` collection access | **Semi-auto** — Liquid-comment suggestion inserted above the original; user reviews diff | +| **Liquid contains adx references** | Embedded `{% fetchxml %}` blocks | Caught by the FetchXML rewriter (above) | +| **Liquid contains adx references** | Property access (`page.adx_X`, `website.adx_X`, etc.) | Categorized as **false positive** — runtime resolves via documented `[logical_name]` accessor | +| **Liquid contains adx references** | `{% editable obj 'adx_X' %}` editable tag | Categorized as **false positive** — parameter is a logical attribute name | +| **Liquid contains adx references** | `snippets[...]` / `weblinks[...]` lookup keys | Categorized as **false positive** — index is a user-defined name, not an attribute | +| **Data Model Extensions** | Custom columns on `adx_*` tables | **Per-table checklist** — grouped by source table; checklist suggests new custom table name, lookup-to-`powerpagecomponent` column, and data-migration steps. **No Dataverse API calls** — schema decisions stay with the user | +| **Plugins registered on adx entities** | `Microsoft.*` system plugins | Categorized as **no action needed** (Power Pages Core handles on EDM) | +| **Plugins registered on adx entities** | `Adxstudio.*` framework plugins | Recommendation: verify V2 EDM-compatible solution installed (step 1.6 check) | +| **Plugins registered on adx entities** | Custom plugins | Per-finding refactor recommendation with original entity + step name | +| **Custom workflow** | Any | Generic doc guidance (no per-finding info available without Dataverse queries) | +| **Relationships between custom and adx tables** | (not in sample reports) | Manual guidance in SKILL.md if encountered | + +### Phase 2 workflow + +```text +1. Has cwd got the site downloaded? Yes → confirm work is committed + No → pac pages download --path ./mysite +2. Run --automate-fetchxml → file-level regex rewrites staged to remediation-staged/ +3. Run --automate-liquid → annotated suggestions staged to remediation-staged/ + (merges on top of step 2 if the file overlaps) +4. Script emits remediation-diff.json → structured per-file manifest powering the Remediation Diff + card in the live execution report +5. Review in live report → user expands per-file hunks inline OR clicks "Open staged + file" to view in VSCode's diff editor +6. User decides: + - Approve → apply-remediation.js copies staged → live, deletes remediation-staged/ + pac pages upload pushes the (now applied) source back to Dataverse + - Discard → apply-remediation.js --discard nukes remediation-staged/, live source untouched + - Edit → user hand-edits files in remediation-staged/; the report refreshes; + re-ask approval +7. Show manual reminders → DME per-table checklists, plugin recs, etc. +8. Final readiness gate → user confirms before step 3.1 (migration) +``` + +The auto-rewriter is non-destructive by construction: it never writes to ``. All proposed changes live under `/remediation-staged/` until the user explicitly approves. `apply-remediation.js` is the only script that touches `` after the user picks "Approve" — and even then only with `copyFileSync` from the staged copy, so a partial apply leaves the staged tree intact for re-runs. + +### Per-finding categorization logic + +The skill executes local-only analysis (no Dataverse API) for every customization finding: + +- **`categorizeLiquidFinding(snippet)`** — pattern-matches the snippet against `entities[...]`, `{% fetchxml %}`, `{% editable %}`, property access, and lookup-key patterns; emits one of `needs-rewrite` / `auto-fetchxml` / `false-positive` / `unknown` with a tailored action message. +- **`categorizePlugin(snippet)`** — name-prefix match on `Microsoft.*` / `Adxstudio.*` / custom; emits per-finding action including original entity and step name. +- **`buildDataModelExtensionChecklists(items)`** — groups column findings by source `adx_*` table; produces one checklist per source table with suggested new-table name and step-by-step guidance from the migration doc. + +Output is rendered in `sdm-to-edm-migration-report.html` under the "Liquid Findings — Categorized", "Plugin Findings — Categorized", "Auto-applied Rewrites", "Data Model Extensions — Per-table Remediation Checklists", and the augmented-prompts sections. + +See `assets/sdm-to-edm-migration-report.html` for the rendered remediation guidance shown to users. + +--- + +## Augmented Prompts for Customer-Owned Code + +Two customization categories — **custom plugins** and **Data Model Extensions** — involve modifying code or schema that the skill does NOT own: + +- **Plugins** live in the customer's plugin source repo (often a separate code repository) +- **DME (custom columns on adx_* tables)** require Dataverse schema changes that should land via a reviewable solution package, not direct API calls + +For both, the skill follows a **paste-ready augmented-prompt** pattern: + +1. The script generates a complete, self-contained prompt tailored to the user's actual findings +2. The prompt is written to a `.txt` file in `/` and embedded in `sdm-to-edm-migration-report.html` +3. The user opens a fresh Claude Code session pointed at the relevant working directory (their plugin repo for plugins; any working dir for DME) +4. The user pastes the prompt as the first message +5. The receiving session performs the work — refactoring plugin code OR building a Dataverse solution package — and surfaces a diff or artifact for the user to review before applying + +### Why this design + +| Concern | Direct execution from this skill | Augmented prompt approach | +| --- | --- | --- | +| Customer-owned plugin source | Skill would need access to the plugin repo — not available | User runs the prompt where the repo is — clean separation | +| Dataverse schema changes | Direct API calls are hard to undo, hard to review, bypass ALM | Solution package is a reviewable artifact; user imports it themselves | +| Decision-making (publisher prefix, column types, on-delete behavior) | Lots of interactive prompts in our skill | Batched in the receiving session | +| Source control | Changes hit Dataverse / customer repo invisibly | All artifacts version-controllable as files | + +### Template storage + +Prompt templates live as static text files under `scripts/prompts/`: + +- `plugin-remediation.template.txt` — placeholder: `{{PLUGIN_FINDINGS_BLOCK}}` +- `dme-remediation.template.txt` — placeholder: `{{DME_TABLE_GROUPS_BLOCK}}` + +The script's `loadPromptTemplate()` reads the file and substitutes the placeholder with the actual findings (markdown-formatted tables / groupings) before writing to `/plugin-remediation-prompt.txt` and `/dme-remediation-prompt.txt`. + +### Surfacing to the user + +All three locations cover different user contexts: + +1. **Terminal output** at the end of script run — visual separator banner with file paths and copy-paste instructions +2. **Standalone `.txt` files** in `/` — for users who want the prompts without keeping the HTML open +3. **HTML execution report** — embedded inside collapsible `
` blocks with copy-to-clipboard buttons (uses `navigator.clipboard.writeText` — self-contained, no external JS) + +### What's covered today + +| Category | Prompt? | Notes | +| --- | --- | --- | +| Custom plugins | ✅ Yes | Refactor pattern, build/test/deploy guidance, no production push | +| Data Model Extensions | ✅ Yes | Solution-package output; data-migration step documented but not packaged | +| Custom-to-adx relationships | ✅ (within DME prompt) | Receiving session can add relationships to the same solution | +| Custom workflows | ❌ No (yet) | Would require per-workflow Dataverse queries to be useful; generic doc guidance in HTML report for now | + +--- + +## Component Type Reference Table + +For FetchXML and Liquid rewrites, map `adx_*` entity → `powerpagecomponenttype` value: + +| Component | Type Value | +| --- | --- | +| Publishing State | 1 | +| Web Page | 2 | +| Web File | 3 | +| Web Link Set | 4 | +| Web Link | 5 | +| Page Template | 6 | +| Content Snippet | 7 | +| Web Template | 8 | +| Site Setting | 9 | +| Web Page Access Control Rule | 10 | +| Web Role | 11 | +| Website Access | 12 | +| Site Marker | 13 | +| Basic Form | 15 | +| Basic Form Metadata | 16 | +| List | 17 | +| Table Permission | 18 | +| Advanced Form | 19 | +| Advanced Form Step | 20 | +| Advanced Form Metadata | 21 | +| Poll Placement | 24 | +| Ad Placement | 26 | +| Bot Consumer | 27 | +| Column Permission Profile | 28 | +| Column Permission | 29 | +| Redirect | 30 | +| Publishing State Transition Rule | 31 | +| Shortcut | 32 | +| Cloud Flow | 33 | +| UX Component | 34 | + +--- + +## Key PAC CLI Commands + +```powershell +# Auth & discovery +pac auth create -u +pac auth who +pac pages list -v + +# Dependency & template-package verification (first-party solutions — flag is required) +pac solution list --includeSystemSolutions + +# Existing migration state +pac pages migrate-datamodel --webSiteId --checkMigrationStatus --verbose +pac pages migrate-datamodel --webSiteId --resetMigration + +# Customization report +pac pages migrate-datamodel --webSiteId --siteCustomizationReportPath + +# Migrate +pac pages migrate-datamodel --webSiteId --mode configurationData +pac pages migrate-datamodel --webSiteId --mode configurationDataReferences +pac pages migrate-datamodel --webSiteId --mode all + +# Poll status +pac pages migrate-datamodel --webSiteId --checkMigrationStatus + +# Flip data model version +pac pages migrate-datamodel --webSiteId --updateDatamodelVersion --portalId + +# Rollback to SDM +pac pages migrate-datamodel --webSiteId --revertToStandardDataModel --portalId +``` + +--- + +## Migration Status Values + +From PAC source (`bolt.module.paportal/sitecustomizations/configurations/Constants.cs`): + +| Status | Value | Skill Behavior | +| --- | --- | --- | +| `NotStarted` | 746610000 | No prior migration; proceed normally | +| `Running` | 746610001 | Prompt user: Wait / Reset / Exit | +| `Completed` | 746610002 | Prior migration done; skip to step 3.2 if version not yet flipped | +| `Failed` | 746610003 | Show last step + errors; offer Retry / Stop | +| `Reverted` | 746610004 | Site was rolled back; proceed as fresh start | +| `Unknown` | 0 | Warn user; ask whether to proceed | + +`--resetMigration` is non-destructive to migrated data — it only flips the tracker status from `Running` → `Failed` so a new migration can be triggered. + +--- + +## Known Limitations + +1. **5K record batch limit** — Migration processes records in batches of 5,000. Large sites can take hours. +2. **Preview feature** — Not GA; behavior may change. +3. **EDM template solutions required** — Every template needs its corresponding V2 solution installed in the environment (see SKILL.md step 1.6 mapping). Missing V2 solutions can be provisioned by creating a dummy EDM site with the same template (the dummy can be deleted after). +4. **PAC CLI version dependency for D365 portals** — Migration of D365 portal templates (Community, Customer Self-Service, Employee Self-Service, Partner) requires a recent PAC CLI build. Older builds may still reject these templates. +5. **Portal Id column** — Available in `pac pages list -v` output only on PAC CLI builds with the 2026-02-24 commit (PR 14824169) or later. Older builds fall back to manual `_services/about` lookup in step 3.2. +6. **30-minute polling ceiling** — The skill polls migration status for 30 minutes, then escalates to a wait/reset/exit prompt. PAC's own server-side migration continues regardless of skill polling. + +--- + +## Production Migration Strategy (Advisory Only) + +The Microsoft documentation recommends creating a full environment copy before production migration. If feasible: + +1. (Recommended) Create a copy of production via Power Platform admin center. +2. Run the full skill on the copy to validate. +3. Add site configuration data to a managed solution. +4. Import the managed solution to production. +5. Re-run the skill on production with `--mode configurationDataReferences` for non-configuration data. +6. Flip the data model version on production. +7. Conduct production validation. + +The skill surfaces this as advisory guidance only. Environment copy is an admin-center operation outside PAC CLI's API surface. Schedule production migrations during non-business hours. + +--- + +## Future Work + +- **ALM integration**: step 1.7 captures environment type but does not yet branch on it for ALM-aware mode selection or remediation deployment. Plan: integrate with a future ALM-deployment skill so Test/UAT/Prod can consume fixes from Dev via managed solutions. +- **Resumable polling**: Persist a `.migration-state.json` keyed by WebSiteId so multi-session migrations can resume without re-running pre-checks. Currently step 1.4 detects in-flight state from PAC's server-side tracker, which is sufficient for most cases. +- **Bulk-site migration**: Currently single-site. A wrapper skill could iterate `pac pages list -v` output and sequence migrations. +- **Data Model Extension table-creation automation**: step 2.2 currently produces per-table checklists with suggested table names. A future enhancement could pre-populate Dataverse via API given publisher prefix and column-type input — but schema decisions remain user-driven, so this is deliberately deferred. +- **Per-workflow guidance**: Custom workflow remediation is generic doc-text today. Adding Dataverse queries to fetch each workflow's primary entity and step bindings would let us emit per-finding guidance, similar to plugins. +- **Automated post-migration validation test cases (Phase 4)**: The current Phase 4 surfaces a manual validation checklist (browse pages, test forms, verify auth, etc.). A future enhancement could add automated functional tests — Playwright-driven smoke tests for page rendering, form submission, web API calls, and authentication flows — so the user gets pass/fail signal instead of a manual to-do list. diff --git a/plugins/power-pages/skills/migrate-datamodel/Migrate SDM to EDM [Design doc].pdf b/plugins/power-pages/skills/migrate-datamodel/Migrate SDM to EDM [Design doc].pdf new file mode 100644 index 000000000..220d72a5e Binary files /dev/null and b/plugins/power-pages/skills/migrate-datamodel/Migrate SDM to EDM [Design doc].pdf differ diff --git a/plugins/power-pages/skills/migrate-datamodel/REPORTS_INTEGRATION.md b/plugins/power-pages/skills/migrate-datamodel/REPORTS_INTEGRATION.md new file mode 100644 index 000000000..d674b14d8 --- /dev/null +++ b/plugins/power-pages/skills/migrate-datamodel/REPORTS_INTEGRATION.md @@ -0,0 +1,310 @@ +# HTML Reports Integration Guide + +This document explains how to integrate the HTML report templates and generation scripts into the `migrate-datamodel` skill workflow. + +## Folder Structure + +``` +plugins/power-pages/skills/migrate-datamodel/ +├── assets/ +│ ├── customization-report.html # Template for customization report +│ ├── sdm-to-edm-migration-report.html # Template for execution report +│ └── README.md # Template documentation +├── scripts/ +│ └── generate-migration-reports.js # Utility to generate reports from data +├── SKILL.md +└── DESIGN.md +``` + +## Workflow Integration + +### Phase 8: Customization Report & Analysis + +**Current flow (SKILL.md):** + +1. Download customization report via PAC CLI +2. Parse and categorize findings +3. Generate HTML report +4. Present findings to user + +**Implementation:** + +```bash +# Phase 8 step 1: Download Customization Report +pac pages migrate-datamodel --webSiteId "" --siteCustomizationReportPath "./migration-report" + +# Phase 8 step 2: Parse findings +# ... parsing logic ... + +# Phase 8 step 3: Generate HTML report +node "${CLAUDE_PLUGIN_ROOT}/skills/migrate-datamodel/scripts/generate-migration-reports.js" \ + --customization-report "./migration-report/SiteCustomization.csv" \ + --site-name "" \ + --website-id "" \ + --template-name "" \ + --output-dir "./migration-reports" + +# Share with user +echo "Customization report: file://$(pwd)/migration-reports/customization-report.html" +``` + +### Phase 9: Automated Remediation (subset) + +For automatable fixes (Data Model Extensions only), the same script is invoked with `--automate` and `--env-url`: + +```bash +node "${CLAUDE_PLUGIN_ROOT}/skills/migrate-datamodel/scripts/generate-migration-reports.js" \ + --site-name "" \ + --website-id "" \ + --siteCustomizationReportPath "./migration-report/SiteCustomization.csv" \ + --env-url "https://org.crm.dynamics.com" \ + --automate \ + --environment-type "" \ + --output-dir "./migration-reports" +``` + +The script creates missing string attributes via Dataverse Web API and logs results into the execution report. All other customization types (Liquid, FetchXML, plugins, workflows) are flagged as manual. + +### Phase 12: Execution Report & Summary + +**Current flow (SKILL.md):** + +1. Present validation checklist +2. Run rollback (if needed) +3. Generate execution report +4. Present final summary + +**Implementation:** + +```bash +# During Phases 1–11: skill collects timing and results +# Track all commands, results, and timing + +# At end of Phase 12: generate execution report +node "${CLAUDE_PLUGIN_ROOT}/skills/migrate-datamodel/scripts/generate-migration-reports.js" \ + --site-name "" \ + --website-id "" \ + --portal-id "" \ + --template-name "" \ + --execution-data "phase1,phase2,phase3,phase4,phase5,phase6,phase7,phase8,phase9,phase10,phase11,phase12" \ + --output-dir "./migration-reports" + +# Share with user +echo "Execution report: file://$(pwd)/migration-reports/sdm-to-edm-migration-report.html" +``` + +## Data Structures + +### Customization Report CSV (from PAC CLI) + +The CSV contains: +- **Type of customization** — Category (Liquid, Data Model Extension, Plugin, etc.) +- **Guidance** — Microsoft link to remediation docs +- **Snippet** — Code snippet or detail +- **Location** — File path or table name + +Example: +``` +Liquid contains adx references,https://go.microsoft.com/fwlink/?linkid=2247170,"{% assign homeurl = website.adx_partialurl %}","web-templates/header/Header.webtemplate.source.html" +Data Model Extension,https://go.microsoft.com/fwlink/?linkid=2247170,"Table name : adx_ad Column name : mspp_websiteid","Table name : adx_ad" +``` + +### Execution Data Structure (to be tracked by skill) + +During skill execution, collect: + +```json +{ + "siteName": "Contoso Portal", + "websiteId": "076bf556-9ae6-ee11-a203-6045bdf0328e", + "portalId": "07f35d71-c45a-4a05-9702-8f127559e48e", + "templateName": "Starter layout 1", + "startTime": "2024-04-20T10:30:00Z", + "prerequisites": [ + { + "name": "PAC CLI Version", + "required": "1.31.6 or higher", + "actual": "1.32.1", + "status": "success" + } + ], + "pacCommands": [ + { + "step": 1, + "description": "Verify Authentication", + "command": "pac auth who", + "status": "success", + "output": "Authenticated to https://org12345.crm.dynamics.com" + }, + { + "step": 2, + "description": "List Available Sites", + "command": "pac pages list", + "status": "success", + "output": "Found 3 websites" + } + ], + "phases": [ + { + "number": 1, + "title": "Verify Prerequisites", + "status": "completed", + "results": [ + { + "type": "success", + "title": "PAC CLI Verified", + "description": "PAC CLI version 1.32.1 meets minimum requirement" + } + ] + } + ], + "customizations": { + "liquidReferences": 8, + "dataModelExtensions": 15, + "pluginsRegistered": 2, + "customWorkflows": 1 + }, + "remediationRequired": true, + "remediationSteps": [ + { + "type": "Liquid References", + "count": 8, + "guidance": "Replace adx_* Liquid objects with powerpagecomponent equivalents" + } + ], + "endTime": "2024-04-20T10:45:00Z" +} +``` + +## Integration Points in SKILL.md + +### Phase 8: Customization Report Generation + +The skill calls the script after parsing the CSV from PAC CLI: + +```powershell +node "${CLAUDE_PLUGIN_ROOT}/skills/migrate-datamodel/scripts/generate-migration-reports.js" ` + --customization-report "" ` + --site-name "" ` + --website-id "" ` + --template-name "" ` + --output-dir "" +``` + +Open the generated HTML report in your browser: `file:///customization-report.html` + +### Phase 9: Automated Remediation + +The same script runs with `--automate` to apply safe fixes via Dataverse API (Data Model Extensions only): + +```powershell +node "${CLAUDE_PLUGIN_ROOT}/skills/migrate-datamodel/scripts/generate-migration-reports.js" ` + --site-name "" ` + --website-id "" ` + --siteCustomizationReportPath "" ` + --env-url "" ` + --automate ` + --environment-type "" ` + --output-dir "" +``` + +### Phase 12: Final Execution Report + +At the end of post-migration validation: + +```powershell +node "${CLAUDE_PLUGIN_ROOT}/skills/migrate-datamodel/scripts/generate-migration-reports.js" ` + --site-name "" ` + --website-id "" ` + --portal-id "" ` + --template-name "" ` + --output-dir "" +``` + +The execution report includes: + +- All PAC commands executed and their results +- Prerequisite verification status +- Migration phase details +- Customization analysis summary +- Remediation guidance +- Post-migration validation checklist +- Next steps + +Open the report in your browser: `file:///sdm-to-edm-migration-report.html` + +## Using `browser_navigate` to Open Reports + +In the Claude skill, after generating reports, use Playwright to open them in the user's browser: + +```javascript +// Open customization report +await browser_navigate(`file://${path.resolve('./migration-reports/customization-report.html')}`); + +// Take accessibility snapshot +const snapshot = await browser_snapshot(); +console.log('Report loaded successfully'); +``` + +Or inform the user of the file path for manual opening: + +``` +I've generated two detailed reports in the `migration-reports` folder: + +1. **Customization Report**: Shows all customizations found + Open: file://${pwd}/migration-reports/customization-report.html + +2. **Execution Report**: Shows all migration steps and results + Open: file://${pwd}/migration-reports/sdm-to-edm-migration-report.html + +You can open these files in your browser to review the details. +``` + +## Development Notes + +### Template Customization + +If you need to modify the templates: + +1. Edit `customization-report.html` or `sdm-to-edm-migration-report.html` in the `assets/` folder +2. Update the placeholder documentation in `assets/README.md` +3. Update the placeholder replacement logic in `generate-migration-reports.js` + +### Adding New Customization Types + +To add a new customization type badge: + +1. Add the CSS class in the template (e.g., `.badge-newtype { ... }`) +2. Update the `badgeMap` in `generate-migration-reports.js` +3. Update the documentation + +### CSV Parsing + +The script uses a built-in CSV parser (no npm dependency) — see `parseCSV()` in `generate-migration-reports.js`. It handles quoted values, embedded quotes (`""` → `"`), and `\r\n` line endings. + +### Dependencies + +The script imports only: + +- Node stdlib: `fs`, `path` +- The plugin-shared validation helpers at `plugins/power-pages/scripts/lib/validation-helpers.js` (provides `getAuthToken`, `makeRequest`, `getEnvironmentUrl`) + +No `npm install` is required to run the script. + +## Sample Output + +After running the generation script: + +``` +✓ Customization report generated: C:\path\to\migration-reports\customization-report.html +✓ Execution report generated: C:\path\to\migration-reports\sdm-to-edm-migration-report.html + +Reports generated successfully! +Open in browser: file:///C:/path/to/migration-reports/customization-report.html +``` + +Both HTML files are self-contained (no external dependencies) and can be: +- Opened directly in any modern browser +- Saved for later reference +- Included in documentation +- Shared with stakeholders diff --git a/plugins/power-pages/skills/migrate-datamodel/SKILL.md b/plugins/power-pages/skills/migrate-datamodel/SKILL.md new file mode 100644 index 000000000..8047c3b97 --- /dev/null +++ b/plugins/power-pages/skills/migrate-datamodel/SKILL.md @@ -0,0 +1,1914 @@ +--- +name: migrate-datamodel +description: >- + This skill should be used when the user asks to "migrate to enhanced data model", + "migrate from standard to enhanced", "switch to EDM", "migrate SDM to EDM", + "upgrade data model", "migrate site data model", or wants to migrate an existing + Power Pages site from the Standard Data Model (SDM) to the Enhanced Data Model (EDM) + using PAC CLI. +user-invocable: true +argument-hint: Optional site name or WebSiteId GUID +allowed-tools: Read, Bash, Glob, Grep, AskUserQuestion, TaskCreate, TaskUpdate, TaskList +model: sonnet +--- + +> **Plugin check**: Run `node "${PLUGIN_ROOT}/scripts/check-version.js"` — if it outputs a message, show it to the user before proceeding. + +> **Plugin check**: Run `node "${CLAUDE_PLUGIN_ROOT}/scripts/check-version.js"` — if it outputs a message, show it to the user before proceeding. + +# Migrate Power Pages Site from Standard to Enhanced Data Model + +Guide the user through a comprehensive migration of an existing Power Pages site from the Standard Data Model (SDM) to the Enhanced Data Model (EDM). The skill is organized into **four high-level phases** — Site Discovery & Pre-checks, Customization Remediation, Migration Execution, and Post-Migration Validation. Each phase contains numbered sub-steps for granular execution and progress tracking. + +> **Important:** This is a preview feature. EDM migration behavior may change before GA. + +## Core Principles + +- **Environment-aware**: Capture environment type (Dev/Test/UAT/Prod) for context and future ALM integration +- **Remediate before migrate**: Identify and fix customizations before executing the data model migration +- **Validate comprehensively**: Check CLI context, site discovery, dependencies, and templates before any execution +- **Confirm before executing**: Present all migration parameters and customization findings to user before proceeding +- **Track all operations**: Generate comprehensive reports documenting all commands, results, and fixes applied +- **Graceful failure**: Halt on blocking issues; guide user to support when needed + +**Supported templates:** All Power Pages and D365 portal templates can be migrated, provided the corresponding V2 EDM solution is installed in the environment (see step 1.6). This includes Starter Layouts 1–5, Application Processing, Blank Page, Program Registration, Schedule and Manage Meetings, FAQ, Event Registration, and the D365 portal templates (Community, Customer Self-Service, Employee Self-Service, Partner). + +> **PAC CLI version note:** Migration of D365 portal templates requires a recent PAC CLI build. Older builds may still reject these templates. + +**Initial request:** $ARGUMENTS + +--- + +## Live Execution Report + +Throughout this skill, progress is mirrored to two files inside ``: + +- `migration-state.json` — single source of truth for current state +- `sdm-to-edm-migration-report.html` — auto-regenerated from state after every update; user opens in browser to watch progress + +**Init point.** As soon as `` is resolved (end of step 1.2) AND WebSiteId is known (from `$ARGUMENTS=GUID`, `website.yml`, or step 1.3 site discovery), run once: + +```powershell +node "${CLAUDE_PLUGIN_ROOT}/skills/migrate-datamodel/scripts/update-state.js" --init --output-dir "" --website-id "" +``` + +After init, each sub-step below ends with a **→ Update report** callout. Execute each as a `node update-state.js --output-dir "" ` call. The CLI accepts: + +| Command | Use | +|---|---| +| `--set-step --status --output ""` | Mark sub-step status + write its output line (status: `pending`/`in-progress`/`completed`/`blocked`) | +| `--set-phase --status ` | Mark phase status (`pending`/`in-progress`/`completed`/`blocked`) | +| `--set-site ''` | Update site card fields. Allowed keys: `name`, `portalId`, `slug`, `currentDataModel`, `template`, `environment`, `migrationMode`, `siteRoot` | +| `--set-approval ` | Show approval banner; `kind` ∈ {`phase-start`, `in-phase`} | +| `--clear-approval` | Hide approval banner once user has approved (use only for in-phase gates that don't advance to a new phase) | +| `--approve-and-start ` | Atomic: clear approval gate + mark phase in-progress in one render. Use this whenever the user answers a `phase-start` AskUserQuestion approval — avoids the transient "Awaiting Approval" pill that would otherwise flash between `--clear-approval` and `--set-phase`. | +| `--set-prompt --status ready --path "" --summary ""` | Show augmented-prompt card | +| `--set-activity ""` / `--clear-activity` | "Currently doing" text for long-running ops | +| `--render-only` | Re-render HTML from existing state (rare) | + +> **Best-effort:** if any `update-state.js` call fails (e.g., node missing), log a warning and continue — the live report is informational, never a blocker for migration work. + +> **Use the `Bash` tool for `update-state.js` calls, not `PowerShell`.** PowerShell parses `(...)` as subexpression syntax inside double-quoted strings, so JSON values containing parens — e.g., `"currentDataModel":"Standard (SDM)"` — error out with `'SDM' is not recognized as a name of a cmdlet`. The `Bash` tool runs through sh, which doesn't have this gotcha; single-quoted JSON works cleanly. The `update-state.js` script itself is pure Node and runs identically from either shell. +> +> If you must use PowerShell (e.g., automation requires it), avoid parens and em-dashes inside JSON values — use plain text like `"Standard SDM"` instead of `"Standard (SDM) — eligible for migration"`. + +### Pointing the user at the report + +The skill **opens the live report in the user's default browser exactly once** — right after `--init` succeeds at Checkpoint 1 (see step 1.2). On every subsequent state change, the file is rewritten in place, so the user just refreshes the already-open tab. After each major state change, **explicitly tell the user where the report is and prompt them to refresh** so they can verify before approving the next phase. The user shouldn't have to guess where files live. Use this pattern in the chat: + +``` +📄 Live execution report updated: + \sdm-to-edm-migration-report.html + +Refresh the browser tab opened in Checkpoint 1 to review the plan/results before approving the next phase. (If you closed it, re-open the path above in your browser.) +``` + +Surface that callout at these **7 checkpoints**: + +| # | Checkpoint | Files to point at | +|---|---|---| +| 1 | After `--init` runs (end of step 1.3) | `sdm-to-edm-migration-report.html` (initialized state) | +| 2 | End of Phase 1, before Phase 2 approval | `sdm-to-edm-migration-report.html` (shows full Phase 1 outcomes + plan for next phase) | +| 3 | After step 2.3 (Authoring Track) — customization CSV parsed | `customization-report.html` (findings catalog) | +| 4 | Mid-step 2.4 (Authoring Track) — after auto-rewrites are staged, before apply+upload approval | `remediation-diff.json` + `remediation-staged/` tree, augmented prompt files (review the **Remediation Diff** card in the live report) | +| 5 | End of Phase 2, before Phase 3 approval | `sdm-to-edm-migration-report.html` (shows Phase 2 outcomes) | +| 6 | End of Phase 3, before Phase 4 approval | `sdm-to-edm-migration-report.html` (shows EDM activation status) | +| 7 | Phase 3.1 (Data Diff Validation — pre-refs gate) | `migration-data-diff.json` + `sdm-to-edm-migration-report.html` (Pages & Components card now shows SDM↔EDM per-category pills) | + +The skill-level approval prompt that follows (via AskUserQuestion) is for the **user's substantive approval to proceed** — not Claude Code's standard command-execution permission prompt. Always print the file paths in chat first so the user knows where to look. + +--- + +## Phase 1: Site Discovery & Pre-checks + +**Goal**: Gather all context needed to plan and execute migration safely. Establish CLI context, identify the target site, detect any prior migration state, verify dependencies and template packages, and select migration mode. + +**Output**: Site identified and confirmed SDM, prior migration state known, dependencies verified, migration mode chosen. Durable values captured: ``, ``, WebSiteId, Portal Id (if available), template name, migration mode. + +--- + +### 1.1 Establish CLI Context + +**Goal**: Set up PAC CLI with correct version and establish authenticated connection to Dataverse + +**Actions**: + +1. **Create todo list** with the 4 high-level phases (see [Progress Tracking](#progress-tracking) table). Sub-steps within each phase are internal execution detail — agent doesn't need to surface them as separate todos. + +2. **Check PAC CLI Installation** + + ```powershell + pac --version + ``` + + - **If version >= 1.31.6**: Proceed to step 3. + - **If not installed or version < 1.31.6**: Ask user: + + | Question | Header | Options | + |----------|--------|---------| + | PAC CLI is not installed or below v1.31.6. Would you like guidance on installation? | Install PAC CLI | Yes, guide me, I'll install manually | + + If "Yes, guide me": Provide OS-specific installation steps from + +3. **Check Existing Authentication** + + ```powershell + pac auth list + pac auth who + ``` + + - **If authenticated**: Extract environment URL and ask user: + + | Question | Header | Options | + |----------|--------|---------| + | Current environment: ``. Is this correct for migration? | Confirm Env | Yes, correct, No, switch environment | + + - If "No": Run `pac auth select` to switch + - If "Yes": Proceed + + - **If not authenticated**: Ask for environment URL and run: + + ```powershell + pac auth create -u "" + ``` + +4. **Inform About Requirements** (user must verify manually): + - Role: System Administrator, Dynamics 365 Admin, or Power Platform Admin + - Dataverse base portal package: 9.3.2307.x+ + - Power Pages Core package: 1.0.2309.63+ + - Environment mode: If admin mode, background operations must be enabled (warning only) + +**Output**: PAC CLI installed/verified, authenticated to correct environment + +--- + +### 1.2 Identify Site Context + +**Goal**: Determine target site from local `website.yml` or user input, AND resolve where to place migration outputs + +**Actions**: + +1. **Resolve site location** + + Run these checks in order to determine `` (where the site source lives or will be downloaded to) and `` (the parent directory under which step 1.3 will create a per-migration subfolder named `--/` for all migration artifacts): + + ```powershell + # Check A: is cwd itself a site root? + Test-Path .\website.yml + + # Check B: if A is False, is there a single subdirectory containing a site root? + Get-ChildItem -Directory | Where-Object { Test-Path (Join-Path $_.FullName 'website.yml') } + ``` + + Resolve based on results: + + | Result of check A | Result of check B | `` | `` | + |---|---|---|---| + | True (cwd is the site) | — | `.` (cwd) | `..\migration-reports` (sibling of site dir, so site source stays clean) | + | False | Exactly one subdir | `.\` | `.\migration-reports` (in cwd alongside the site folder) | + | False | Zero or multiple subdirs | (will download to `/site-sdm/` in step 2.1) | `.\migration-reports` (in cwd) | + + Capture both `` and `` as durable values. The actual per-migration subfolder `` is computed in step 1.3 and is used throughout the rest of the skill. Create `` if it doesn't exist yet. + +2. **Use website.yml if present** + + If a `website.yml` exists at ``, parse it and extract: + - `adx_name` (site name) + - `adx_websiteid` (site GUID) + + Ask user confirmation: + + | Question | Header | Options | + |----------|--------|---------| + | Found local website.yml for site: `` (ID: ``) at ``. Use this site? | Use Local Context | Yes, use this site, No, specify different site | + + - If "Yes": use extracted values; continue to step 1.3. + - If "No": treat as if no website.yml; proceed to step 3. + +3. **Get site identification from user** + + If no `website.yml` resolved or user declined, ask: + + | Question | Header | Options | + |----------|--------|---------| + | Provide the site name or WebSiteId (GUID) for migration | Site ID | I'll paste the site name, I'll paste the WebSiteId | + + Then prompt for the actual value in the chat (do not create a single-option AskUserQuestion — that errors with "expected array to have >=2 items"; ask in plain text or use 2+ options). + + In this path, `` will be set to the path step 2.1 downloads to (typically `/site-sdm/` once `pac pages download` runs). Downloading into the per-migration subfolder keeps every artifact for this site contained — multiple migrations sharing the same parent dir won't collide on the `site-sdm` / `site-edm` folder names. + +**Output**: WebSiteId captured; `` and `` resolved (or marked "download in step 2.2" for ``) + +> **Live report init is deferred to the end of step 1.3.** The init command needs the environment name and the website slug to compute a per-migration subfolder; both are captured by `pac auth list` (step 1.1) and `pac pages list -v` (step 1.3) respectively. We don't write `migration-state.json` here in 1.2. + +--- + +### 1.3 Site Discovery and Validate Data Model + + + +**Goal**: Find the site in the environment and verify it's on SDM (not already EDM) + +**Actions**: + +1. **List All Sites** + + ```powershell + pac pages list -v + ``` + + Verbose output columns: `Index | Website Id | Portal Id | Friendly Name | Portal Url | Data Model Version | Single Page Application | Is Site Active`. + + Parse output to extract all available sites with: + - WebSiteId + - Portal Id (may render as `Unknown` if the Power Platform active-websites API failed, or `N/A` if the site is inactive — treat both as "missing" for the activation step in Phase 3, which prompts the user when Portal Id is needed) + - Portal Url (full site URL from the `Portal Url` column; may be empty for inactive sites — store as `null` if so) + - Site Name (display name from Friendly Name, the part before " - ") + - URL slug (from Friendly Name, the part after " - ") + - Current ModelVersion (`Standard` or `Enhanced`) + + > **Note:** Template name is not included in `pac pages list` output. Template will be confirmed separately in step 4. + > + > **PAC CLI version note:** The Portal Id column was added on 2026-02-24. If your installed PAC build predates that and the column is missing entirely, treat Portal Id as missing — the activation step in Phase 3 will prompt the user before the data-model update. + +2. **Locate Target Site** + + Search the list for site matching user input (name or GUID): + - If found: Extract WebSiteId, Portal Id, ModelVersion, and URL slug. Store all four for later phases. If Portal Id parsed as a valid GUID, mark it as "captured"; if `Unknown`/`N/A`/missing, mark as "needs prompt". + - If not found: Show list and ask user to confirm site name/ID. If still not found, stop and ask user to verify in Power Platform admin center. + +3. **Validate Data Model** + + Check `ModelVersion` from output: + - **If EDM**: Stop with message: "This site is already on Enhanced Data Model. Migration not needed." + - **If SDM**: Continue to step 1.4 + +4. **Identify Site Template** + + Template name is not available from `pac pages list`. If step 1.4 reads `adx_templatename` from the migration tracker (populated by a prior migration), use that and skip this prompt. + + Otherwise ask the user via a **2-question tree** (Claude Code's `AskUserQuestion` tool caps at 4 options per question, so the flat 16-template list errors out with `InputValidationError: too_big`). Ask the family first, then drill down based on the answer. + + **Q1 — Template family:** + + | Question | Header | Options | + |----------|--------|---------| + | What family is this site's template in? | Template Family | Starter Layout / Blank Page, Power Pages template (FAQ / Event / Application / etc.), D365 portal template, Other or Unknown | + + **Q2a — if "Starter Layout / Blank Page":** + + | Question | Header | Options | + |----------|--------|---------| + | Which Starter Layout or Blank template? | Starter | Starter layout 1, Starter layout 2 / 3, Starter layout 4 / 5, Blank page | + + (If user picks "Starter layout 2 / 3" or "Starter layout 4 / 5", drill once more to the specific number with a 4-option follow-up. Or accept the pair and pick the conservative V2 package — the install probe in step 1.6 will fail if it's wrong and surface the actual name.) + + **Q2b — if "Power Pages template (FAQ / Event / Application / etc.)":** + + | Question | Header | Options | + |----------|--------|---------| + | Which Power Pages template? | Template | FAQ, Event registration, Application processing, Program registration / Schedule and manage meetings | + + **Q2c — if "D365 portal template":** + + | Question | Header | Options | + |----------|--------|---------| + | Which D365 portal? | D365 Portal | Community, Customer Self-Service, Employee Self-Service, Partner | + + **Q2d — if "Other or Unknown":** no follow-up. Treat as Other/Unknown — step 1.6 skips the template-specific V2 package check entirely. The foundation packages (CDSBasePortal, PowerPages_Core) were already validated in step 1.5. + + Store the final chosen template — it drives the V2 package check in step 1.6. + + > **Why the chunking:** `AskUserQuestion` errors with `InputValidationError: questions.0.options: Too big: expected array to have <=4 items` when more than 4 options are passed. The tree shape keeps every individual question within the limit while still letting the user pick from the full 15 supported templates plus Other/Unknown. + +**Output**: Target site confirmed as SDM, template confirmed by user, WebSiteId/ModelVersion/URL slug/Portal Id captured (Portal Id marked "captured" or "needs prompt") + +> **→ Initialize live report (per-migration subfolder).** Now that env name (from step 1.1's `pac auth list`) and website slug (from `pac pages list -v` above) are both known, initialize state. Run: +> +> ``` +> node update-state.js --init \ +> --output-dir "" \ +> --website-id "" \ +> --env-name "" \ +> --slug "" +> ``` +> +> The command creates a per-migration subfolder `/--/` and writes `migration-state.json` + `sdm-to-edm-migration-report.html` inside it. It prints the resolved subfolder path on stdout — **capture that path as `` and use it as `--output-dir` for every subsequent `update-state.js` call and as the artifacts directory throughout the rest of the skill.** Pass `--force` only if you intentionally want to wipe an existing migration in the same subfolder. +> +> Then batch-update the steps that ran before init: +> +> ``` +> --set-step 1.1 --status completed --output "PAC CLI v · auth · env " +> --set-step 1.2 --status completed --output " resolved; parent " +> --set-site '{"name":"","slug":"","portalId":"","portalUrl":"","currentDataModel":"Standard SDM","template":"","siteRoot":""}' +> --set-step 1.3 --status completed --output "Site · ModelVersion=Standard · template