Sync/upstream 2026 05 28 - #1
Merged
Merged
Conversation
…te-to-pipeline lifecycle (microsoft#167) * Setup-solution skill verified for 100 components site * Fix export-solution bugs found during live testing against IdeaSphere - AsyncJobId → AsyncOperationId (actual field name in ExportSolutionAsync response) - Fix statecode table: Succeeded is statecode=3 not 2 - Fix grep case: solution.xml is lowercase in zip, use grep -i Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix import-solution bugs found during live testing against IdeaSphere - ImportJobId → ImportJobKey (actual field name in ImportSolutionAsync response) - StageSolutionUploadId not accepted by ImportSolutionAsync — always use CustomizationFile - Fix solution.xml grep case in import SKILL.md (grep -qi) - Clarify StageSolution is pre-flight only, does not alter import call Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add AttachmentBlocked import error pattern; fix import SKILL.md case bug - deployment-error-catalog: add Pattern 11 for AttachmentBlocked (-2147188706) discovered during live testing against portal environment - import SKILL.md: fix grep -q Solution.xml → grep -qi solution.xml Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add Phase 5b: resolve AttachmentBlocked errors in import-solution When ImportSolutionAsync fails with AttachmentBlocked (-2147188706): 1. Identify which extensions in the solution zip are on the blocked list 2. Explain the issue to the user clearly 3. Ask explicit consent before modifying environment settings 4. Remove only the specific blocked extensions (preserve others) 5. Retry the import Follows same consent pattern as deploy-site Phase 6 (blocked JS handling). Discovered during live testing against the portal environment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Clarify Phase 5b consent: invoke AskUserQuestion directly, not as chat text Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * export-solution: ask managed vs unmanaged via AskUserQuestion directly Replace prose description with explicit AskUserQuestion invocation. User answers live before export proceeds — not via chat text. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Remove er-diagram-ideasphere.png — not part of skill code Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * power pages alm using solutions (#47) * Setup-solution skill verified for 100 components site * Fix export-solution bugs found during live testing against IdeaSphere - AsyncJobId → AsyncOperationId (actual field name in ExportSolutionAsync response) - Fix statecode table: Succeeded is statecode=3 not 2 - Fix grep case: solution.xml is lowercase in zip, use grep -i Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix import-solution bugs found during live testing against IdeaSphere - ImportJobId → ImportJobKey (actual field name in ImportSolutionAsync response) - StageSolutionUploadId not accepted by ImportSolutionAsync — always use CustomizationFile - Fix solution.xml grep case in import SKILL.md (grep -qi) - Clarify StageSolution is pre-flight only, does not alter import call Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add AttachmentBlocked import error pattern; fix import SKILL.md case bug - deployment-error-catalog: add Pattern 11 for AttachmentBlocked (-2147188706) discovered during live testing against portal environment - import SKILL.md: fix grep -q Solution.xml → grep -qi solution.xml Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add Phase 5b: resolve AttachmentBlocked errors in import-solution When ImportSolutionAsync fails with AttachmentBlocked (-2147188706): 1. Identify which extensions in the solution zip are on the blocked list 2. Explain the issue to the user clearly 3. Ask explicit consent before modifying environment settings 4. Remove only the specific blocked extensions (preserve others) 5. Retry the import Follows same consent pattern as deploy-site Phase 6 (blocked JS handling). Discovered during live testing against the portal environment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Clarify Phase 5b consent: invoke AskUserQuestion directly, not as chat text Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * export-solution: ask managed vs unmanaged via AskUserQuestion directly Replace prose description with explicit AskUserQuestion invocation. User answers live before export proceeds — not via chat text. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Remove er-diagram-ideasphere.png — not part of skill code Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add hotfix-solution skill for incremental Power Pages deployments 7-phase skill: discover components modified in a time window, package into a timestamped hotfix solution, export (managed/unmanaged), and import to a target environment with full AttachmentBlocked remediation. - skills/hotfix-solution/SKILL.md — full skill definition - skills/hotfix-solution/scripts/validate-hotfix.js — validates .last-hotfix.json - AGENTS.md — updated with skill entry, hooks, validate script, shared refs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add hotfix-solution skill for incremental Power Pages deployments 7-phase skill: discover components modified in a time window, package into a timestamped hotfix solution, export (managed/unmanaged), and import to a target environment with full AttachmentBlocked remediation. - skills/hotfix-solution/SKILL.md — full skill definition - skills/hotfix-solution/scripts/validate-hotfix.js — validates .last-hotfix.json - AGENTS.md — updated with skill entry, hooks, validate script, shared refs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * Add setup-pipeline and deploy-pipeline skills; remove generate-pipeline (#68) - Replace generate-pipeline (YAML-only stub) with setup-pipeline: full PP Pipelines implementation via OData API, plus GitHub/ADO coming-soon stubs - Add deploy-pipeline: creates stage runs, validates packages, polls stagerunstatus, handles approval gates, retries, and writes .last-deploy.json - cicd-pipeline-patterns.md: major expansion with HAR-confirmed API patterns, source code findings (RetryFailedDeploymentAsync, cancel, scheduled deploy, redeployment, platform host BAP API), and live-test bug fixes (18 total) - References in setup-solution, export-solution, hotfix-solution, solution-api-patterns updated from generate-pipeline → setup-pipeline - AGENTS.md updated with new skill descriptions, hooks, and architecture tree Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * Deployment variables, CI/CD pipeline deploy, and setup-solution improvements (#75) * Add configure-env-variables skill and update deploy-pipeline for env var support - New skill: configure-env-variables — creates environmentvariabledefinition records, adds them to solution (componenttype 380), generates deployment-settings.json with per-stage override values; guides UI step for mspp_sitesetting source linking - deploy-pipeline: reads deployment-settings.json and injects deploymentsettingsjson in stage run PATCH so env var values are overridden per target stage at deploy time - setup-solution: adds configure-env-variables to suggested next steps Validated end-to-end against IdeaSphere: ids_LocalLoginEnabled=true in dev, deployed to staging as false via deploymentsettingsjson injection (stage run abdc2380). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix setup-solution: include tables, Web API settings, and table permissions Analysis of BYOC Supplier Portal solution (BYOCSupplierPortal_1_0_0_1.zip) revealed three gaps that cause deployed sites to not work in target environments: 1. Dataverse tables not in solution: added Step 5.3 to read .datamodel-manifest.json and add each custom table as ComponentType=1 (EntityDefinitions/MetadataId) 2. Site settings split: replaced all-or-nothing site settings prompt with category-based approach — Webapi/* and feature flags included by default, only OAuth secrets (ClientSecret, AppSecret, ConsumerSecret) excluded by default 3. Table permissions (powerpagecomponenttype=18) explicitly called out as always-included Also added Patterns 12/13/14 to deployment-error-catalog.md covering these three failure modes, and documented Dataverse table AddSolutionComponent pattern in solution-api-patterns.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * setup-solution: add site language, full manifest review, table schema note - Site language (powerpagesitelanguage, componenttype ~10375) is NOT auto-included by AddRequiredComponents:true on the website — must be discovered and added explicitly. Confirmed against IdeaSphere: 10375 was missing from solution. - New Step 5.4: present full manifest to user before writing anything — shows website, site language(s), all component categories with counts, site settings split (Web API / feature flags / auth config / OAuth secrets), and Dataverse tables. User can deselect categories or tables before proceeding. - Table schema note: solutions carry entity definitions (columns, relationships, forms) but NOT data/records. Explicit callout added so users know data migration is a separate step. - solution-api-patterns.md: added site language componenttype (~10375) to the known values table + discovery query pattern. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * setup-solution: multi-select OAuth secrets as env vars Phase 5 now has a dedicated Step 5.4 asking which (if any) OAuth secret site settings to convert to environment variables instead of just excluding them. Uses AskUserQuestion with multiSelect:true listing each secret individually, plus 'Convert all' and 'Exclude all' convenience options. Converted secrets get environmentvariabledefinition records (type=Secret/100000003) created and added to the solution as componenttype 380. Phase 7 summary includes a UI linking checklist with direct Power Pages Management URLs for each converted secret (same UI-only limitation as configure-env-variables skill). Step numbering updated: 5.4=OAuth secrets, 5.5=full manifest confirm, 5.6=add components. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * setup-solution: always discover tables from env, include-all as first option - Step 5.2D: always query EntityDefinitions directly (IsCustomEntity=true, IsManaged=false) rather than relying solely on .datamodel-manifest.json. Groups by publisher prefix, presents all custom unmanaged tables found. .datamodel-manifest.json is still checked but not the only source. - Step 5.5 table multi-select: 'Include all N tables (Recommended)' is now the first option (pre-selected default), followed by individual table options, with 'Exclude all tables' last. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * setup-solution: automate OAuth secret linking + add deployment path choice Phase 5.4: link site settings to env vars via OData PATCH (EnvironmentValue nav property + if-match/clienthost headers) — no UI step needed (HAR-verified Fix 19). Phase 7: AskUserQuestion for PP Pipelines (recommended) vs export/import vs later. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * import-solution + deploy-pipeline: env var prompting and post-deploy activation check (#77) * deploy-pipeline: proactively prompt for env var values before deploy import-solution: set env var values in target after import (Phase 6b) deploy-pipeline Phase 5 now queries solution for env var definitions, diffs against deployment-settings.json, and prompts for any unconfigured values before calling DeployPackageAsync — not just when validation flags them. import-solution Phase 6b queries target for env var definitions that landed without values and prompts the user to set them, including Secret type vars that need target-specific values (e.g. different OAuth credentials per env). * Add post-deployment site activation check to import-solution and deploy-pipeline After a successful import or pipeline deploy, both skills now check whether the Power Pages site is already activated in the target environment. If not, the user is asked for consent and /power-pages:activate-site is invoked to handle subdomain selection, confirmation, and provisioning. - import-solution: Phase 6c queries for componentType 10374, runs check-activation-status.js (PAC already on target), and invokes activate-site if not provisioned. - deploy-pipeline: Phase 7.5 does the same but temporarily switches PAC CLI to the target env via `pac env select`, then switches back to source. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * deploy-pipeline: tabbed deploy history + solution contents; plan-alm: per-stage activation (#87) * Add ALM deployment plan skill, 16 shared lib scripts, and solution packaging improvements - New `plan-alm` skill: 8-phase orchestrator that generates an HTML ALM plan (docs/alm-plan.html) with branched question flow for PP Pipelines vs Manual path, then executes the plan by invoking existing skills in sequence - 16 new shared lib scripts in scripts/lib/ covering ALM prerequisites, solution management, PP Pipelines API operations, and solution export/download - 129 unit tests (up from 69) covering all new scripts and the plan-alm validator - Script wiring: setup-solution, setup-pipeline, deploy-pipeline, export-solution, hotfix-solution, configure-env-variables, import-solution all reference shared scripts instead of inline API prose - Cloud flow detect-and-guide in import-solution (Phase 6c) and deploy-pipeline (Phase 7.8): detects type-29 workflow components and guides manual registration - Post-deploy warnings in deploy-pipeline for connection reference binding and bot republishing when solution manifest includes cloudFlows or botComponents - import-solution Phase 2 pre-import zip inspection: warns about connection references, bots, and hardcoded org URLs before import proceeds - solution-api-patterns.md Section 8: documents BYOC vs traditional portal packaging differences for cloud flows and bots - cicd-pipeline-patterns.md: Coming Soon markers on ADO and GitHub Actions sections - Plugin version bumped to 1.2.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * plan-alm: reorder Phase 2 questions — solution always before strategy Q1 is now always solution setup (confirm existing or include setup-solution). Q2 is promotion strategy (PP Pipelines vs Manual). Path-specific questions renumbered Q3–Q8 for both paths. Host env discovery uses discover-pipelines-host.js. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Surface plan-alm as natural next step across site lifecycle skills - setup-solution: Phase 7 now offers plan-alm as the recommended next step (replaces direct route to setup-pipeline/export-solution, which bypassed the orchestrator) - deploy-site: plan-alm added as first suggested next step after deployment - activate-site: plan-alm added to next steps after site provisioning - create-site: plan-alm added to later-stage next steps alongside setup-datamodel/add-seo Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix plan-alm surfacing: activate-site only, revert setup-solution - setup-solution: reverted Phase 7 to original (PP Pipelines / Manual / Decide later) setup-solution is invoked by plan-alm so must not route back to it - deploy-site: removed plan-alm from next steps (wrong lifecycle stage) - create-site: removed plan-alm from next steps (wrong lifecycle stage) - activate-site: plan-alm suggestion retained — correct handoff point after site goes live Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * deploy-pipeline: tabbed deploy history with solution contents; plan-alm: per-stage activate prompt ## deploy-pipeline - Rewrote deploy-history-template.html with vertical sidebar tabs (Overview / Solution / Config & Notes) matching the ALM plan layout - Overview tab: status banner, summary bar, deployment details table, site activation - Solution tab: solution metadata (version change, type, publisher, component count), validation result badge, Dataverse table chips + bot components from .solution-manifest.json - Config & Notes tab: env var overrides, AI deployment notes, post-deploy warnings - Added __SOLUTION_META_ROWS__, __VALIDATION_SECTION__, __SOLUTION_CONTENTS_SECTION__, __PREV_ARTIFACT_VERSION__ placeholders to Phase 7.4 - validate-deploy-pipeline.js: now blocks if deployHistoryFile referenced in .last-deploy.json but not written to disk - validate-deploy-pipeline.test.js: new test file (7 cases) covering all validator paths ## plan-alm - Per-stage activation: changed Phase 3 task creation from one "Deploy via pipeline" task to one Deploy + one Activate task pair per target stage - Phase 7 PP path now loops per stage: deploy → immediately prompt activation → next stage - AskUserQuestion activation prompt fires right after each successful stage deployment, not deferred to end of plan - planData steps use per-stage deploy+activate pairs (Deploy to Staging / Activate site in Staging / etc.) - render-alm-plan.js + alm-plan-template.html: authNoValue rendering fixes, stageClass driven by deployStatus field ## setup-solution - Phase 1 Step 5: pre-load site settings from .alm-plan-context.json if present - Three-tier site settings classification (keepAsIs / promoteToEnvVar / authNoValue / excluded) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * Add solution-splitting decision tree and manifest v2 (#107) * Add solution-splitting decision tree and manifest v2 Introduces an advanced solution-recommendation layer for plan-alm + setup-solution + setup-pipeline + deploy-pipeline, based on the spec in design docs (solution-splitting-logic.md). New shared libs (under plugins/power-pages/scripts/lib/): - alm-thresholds.js: default thresholds + .alm-config.json merge - estimate-solution-size.js: queries Dataverse metadata, estimates size/count/schema attrs/web file aggregates/env vars - compute-split-plan.js: decision tree with priority Schema > Layer > Change-Frequency > Config Isolation (additive). Includes asset advisory generator (Azure Blob primary, CDN secondary). Tests (23 passing): alm-thresholds.test.js, compute-split-plan.test.js cover all five scenarios from the spec (Typical, Feedback Portal, Prabhat 34x950, Brad 1400 envvars, component-heavy) plus advisory edge cases. Template + renderer: - alm-plan-template.html: tabbed UX (Overview, Size, Advisory, EnvVars, Solutions, Pipelines, Execution). Matches reference design doc. - render-alm-plan.js: rewritten to accept v2 planData fields (sizeAnalysis, assetAdvisory, proposedSolutions, breakdown) and render all new tabs. Skill wiring (changes only — no behavior regressions for single-sol sites): - plan-alm SKILL.md: Phase 1 Step 10 adds estimate + compute-split-plan calls. Phase 2 Q1b asks user to accept/override/advisory-first/migrate. Phase 3 planData extended with v2 fields. - setup-solution SKILL.md: Phase 1 Step 6 detects .alm-split-plan.json and enters MULTI_SOLUTION_MODE. Phase 6 writes manifest v2 when multiple solutions are present. - setup-pipeline SKILL.md: reads schemaVersion from manifest; creates one pipeline per solution in v2 mode. Writes .last-pipeline.json v2 with pipelines[] array. - deploy-pipeline SKILL.md: MULTI_PIPELINE_MODE loops through pipelines in order for the selected stage. Sample renders (plugins/power-pages/skills/plan-alm/samples/): - ideasphere-estimate.json: realistic IdeaSphere profile with 128 MB, 5 large media files, 7 env vars. - ideasphere-alm-plan.html: end-to-end sample generated via estimate -> compute-split-plan -> render pipeline. Decision tree correctly selects Strategy 1 (Layer Split) -> 2 solutions. All tabs populated with real classifier output. Plugin version bumped 1.2.0 -> 1.3.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Apply PR #107 review fixes Addresses code-review findings on the solution-splitting PR: - AGENTS.md: remove duplicate "Solution Splitting Decision Tree" section - alm-thresholds.js: rename classifyTier args to make strict-upper-exclusive bounds explicit (greenUpperExclusive, yellowUpperExclusive) + explain - compute-split-plan.js: * isComponentHeavy no longer silently drops sites above hardFlagComponentCount; they now route to Strategy 2 with an error-type recommendation calling out the hard flag. * partitionByChangeFrequency derives sizeMB from count shares so size and componentCount stay self-consistent (was using independent 25/20/10/45% fractions that didn't match the count allocation). * partitionBySchema uses estimate.breakdown.tables when available instead of the hard-coded 50/50 heuristic; falls back with a rationale suffix. - estimate-solution-size.js: exit 1 on auth failure (was exit 0 with empty estimate, which silently clobbered prior good output when callers redirected stdout to a file). Dedupe repeated `token || resolved` pattern. - plan-alm SKILL.md Step 10: use tmp-file write pattern so a failed estimator preserves the previous good .alm-size-estimate.json / .alm-split-plan.json. - deploy-pipeline SKILL.md: document partial-deploy risk for MULTI_PIPELINE_MODE (no automatic rollback; record per-pipeline status in .last-deploy.json). - render-alm-plan.js: comment explaining the single-occurrence plan-status regex. - .gitignore: add ALM split-plan intermediates (.alm-size-estimate.json(.tmp), .alm-split-plan.json(.tmp)). - Tests: * New estimate-solution-size.test.js (7 tests) covering classifyPPCs buckets, empty input, estimateTotalSize math stability + web file additivity, and BYTES_PER sanity. * compute-split-plan.test.js adds hard-flag routing + error recommendation, Change-Frequency size/count consistency (sum ~ totalSizeMB ±0.5), and partitionBySchema breakdown-driven sizing. - Sample: trailing newline on ideasphere-plan-data.json. Three validation items deferred to a live Dataverse run on IdeaSphere dev: OData GUID filter quoting in the estimator, end-to-end multi-solution flow, and hand-authored sample vs. real estimator output. Tracked in auto-memory (project_pr107_deferred_validation.md). Tests: 34/34 ALM unit tests pass (23 pre-existing + 4 new compute-split-plan + 7 new estimate-solution-size). render-alm-plan.test.js failures are pre-existing (the renderer was rewritten in this PR; its tests were not updated) — tracked separately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add ALM-aware-by-default safeguards for Power Pages skills (#108) * Restore missing ALM skill files from PowerPagesALM merge The merge commit 49c93ad registered the merge but failed to bring in any file additions from users/nityagi/PowerPagesALM. This restores all missing power-pages files: setup-pipeline, deploy-pipeline, export-solution, import-solution, configure-env-variables, diagnose-deployment, hotfix-solution, setup-solution SKILL.md files and their validation scripts, plus shared reference docs and lib scripts added in the ALM branch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add ALM-aware-by-default safeguards for Power Pages skills Fixes the recurring bug where Dataverse records created by skills land in the Default solution and never travel across environments — e.g. invoice-checker server logic and crd50_auth_openauth_microsoft_clientsecret env var. New shared modules: - scripts/lib/discover-site-components.js — authoritative site inventory + diff against a solution (14 tests). Covers all 31 documented powerpagecomponenttype values. - scripts/lib/resolve-target-solution.js — strict 3-step solution resolution (explicit arg > .solution-manifest.json > throw); never silently falls back to Default (13 tests). - scripts/lint-skills-alm.js — CI gate: flags SKILL.md that POSTs to Dataverse without reading the manifest, scripts that write without importing the resolver, and new ppctype values missing from PPC_TYPE_LABELS (10 tests). Skill updates: - setup-solution: Phase 1 auto-detects .solution-manifest.json and branches to sync mode; Step 5.4b adopts orphan env vars with the publisher prefix. - export-solution: new Phase 2.5 pre-export completeness check. - deploy-pipeline: new Phase 3.5 pre-deploy completeness check. - create-environment-variable.js: now uses the resolver + adds the created definition via AddSolutionComponent (fixes the crd50_auth_* orphan case). Docs: - AGENTS.md + CLAUDE.md: ALM-aware-by-default principle with the strict solution resolution order. - PLUGIN_DEVELOPMENT_GUIDE.md: new "ALM Checklist for New Skills" section. All 159 plugin tests pass; alm-lint reports 0 findings on the current tree. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Extend alm-lint coverage: CI workflow, PATCH/PUT verbs, allowlist, user docs - .github/workflows/power-pages-alm-lint.yml: new CI job gates PRs that touch plugins/power-pages/** on `node lint-skills-alm.js` passing. - lint-skills-alm.js: PROSE_WRITE_PATTERN now covers POST/PATCH/PUT so skills that mutate existing publishers/solutions are also gated. DELETE is intentionally excluded (resolver does not apply). Helper-name list extended (apiPatch, patchRecord, updateRecord). - lint-skills-alm.js: adds .almlintignore allowlist support. Each entry is `<path-or-glob> <rule-name> <reason-text>` — unknown rules and missing reasons throw at parse time. Inline alm-lint-ignore comments still work for per-file exceptions; the file handles systemic patterns. - plugins/power-pages/references/alm-prompts.md: new user-facing walkthrough of the sync-mode, orphan-adoption, and pre-export completeness prompts. Linked from README.md. - Tests: +11 (verbs, allowlist parsing, glob matching, filtering) — 175/175 pass, alm-lint: 0 findings on the current tree. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address review feedback on ALM safeguards Reject invalid solution unique names and publisher prefixes up front instead of silently stripping non-alphanumeric characters — a typo like "my-sol" no longer becomes a confusing "not found" result. Also guards OData filters from injection earlier in the call path. Replace regex-parsing of PPC_TYPE_LABELS in lint-skills-alm with a direct `require()` of the sibling discover-site-components module, so formatting changes (comments, multi-line entries) can't silently shrink the known-set used by the non-waivable DISCOVER-coverage rule. Normalize rule-name casing in both inline `alm-lint-ignore:` tags and .almlintignore file entries — previously the inline matcher was case-insensitive but the file matcher was case-sensitive, so the same rule name could suppress one path but not the other. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * ALM validation run: 11 fixes + integration tests + live-verified end-to-end (#119) * plan-alm: 3 ALM planning refinements 1. Solutions tab surfaces an Asset Advisory callout. When compute-split-plan recommends 'externalize-media' (bulk media/web-file asset move), the Solutions tab now shows a top-of-section callout summarizing the candidate count + MB and linking to the Asset Advisory tab via the existing nav switch. Without this, reviewers approved splits that a CDN/Blob move would have made unnecessary. - render-alm-plan.js: new buildAssetAdvisoryCallout() - +3 tests covering visible, hidden (no recommendation), and disabled (feature off) cases. 2. Single Power Platform Pipeline with multiple stage runs instead of one pipeline per solution. A PP Pipeline can already deploy different solutions via stage runs that specify different artifactname/solutionid against the same stage — creating N pipeline records was wasteful and cluttered the Pipelines UI. - .last-pipeline.json bumps to schemaVersion: 3 with a single pipelineId + stages[] + deploymentOrder[{ solutionUniqueName, solutionId, order }]. - setup-pipeline Phase 6b: creates ONE pipeline + one stage per target env, records deploymentOrder. v2 (legacy pipelines[]) path documented with a migration prompt on re-run. - deploy-pipeline: new MULTI_RUN_MODE loops over deploymentOrder, creating one stage run per solution against the same stageId. Legacy MULTI_PIPELINE_MODE kept for v2 manifests. - render-alm-plan.js Pipelines tab: "Deployment Pipeline" (singular), shows one pipeline header + a Deployment order block listing per-solution runs. Future buffer surfaces as "Skipped (empty)". - +2 tests covering multi-solution and single-solution paths. 3. Split plans now include a {Prefix}_Future solution reserved for new component additions. Without this, every new server-logic / flow / env var tended to end up crammed into the wrong layer solution and forced a re-plan. - compute-split-plan.js: new appendFutureBuffer() called after partition when the plan has ≥2 solutions. Single-solution plans skip the buffer (no partition to protect). Tagged isFutureBuffer: true, sized at 0 MB / 0 components, componentTypes: ['Any']. - setup-pipeline skips stage-run creation for buffer solutions (status: 'skipped-empty'); deploy-pipeline short-circuits them in the loop so an empty Future does not fail the deploy. - +4 tests (single-solution skip, buffer shape, last-position + naming invariant across strategies, appendFutureBuffer unit-level). - 4 existing tests updated for the new length/ordering. Totals: 326/331 tests pass across the plugin; 5 pre-existing failures from PR #107 (render-alm-plan.test.js scenarios) are unrelated to this change. alm-lint reports 0 findings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * plan-alm + estimator: pre-plan completeness check, OData filter verified Item 28 — plan-alm: pre-plan completeness check. New Phase 1 Step 11 runs discover-site-components.js against the source env and diffs against the current solution. If gaps exist, prompts the user to either run /power-pages:setup-solution in sync mode before planning, plan anyway (records the gap as KNOWN_GAPS so Phase 3 surfaces it in the Risks section), or cancel. Matches the same check already enforced at export (export-solution Phase 2.5) and deploy (deploy-pipeline Phase 3.5) — catches the drift a plan-turn earlier so reviewers do not invest time in stale plans. Items 1–3 — OData GUID filter in estimate-solution-size.js verified. Tested both quoted (`_powerpagesiteid_value eq '<GUID>'`) and unquoted forms against org1e98cc97 (v9.2 endpoint). Both return HTTP 200 with identical result sets. Current code uses the quoted form and continues to work; added a verification comment inline and updated memory/project_pr107_deferred_validation.md with the 2026-04-21 result. Tests: 326/331 pass, alm-lint: 0 findings. The 5 remaining failures are pre-existing PR #107 regressions in render-alm-plan.test.js, unrelated to these changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * estimate-solution-size: fix EntityDefinitions \$top=500 (400 error) EntityDefinitions endpoint returns HTTP 400 "query parameter \$top is not supported" on v9.2. Verified 2026-04-22 against org1e98cc97 — the estimator could never successfully discover custom tables via this path, which made plan-alm unusable on any site that relied on metadata-based table discovery. Replaced \$top=500 with \$filter=IsCustomEntity eq true. Filter is supported on EntityDefinitions even when \$top is not, and it narrows the payload to custom tables without a post-fetch client filter. Inline comment added citing the 2026-04-22 live verification so the next agent doesn't re-add \$top. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * estimator + plan-alm + integration tests: siteTotal vs inSolution + Phase 7.7 trigger Three fixes from today's ALM validation run on SupplierInvoicePortal dev → staging: 1. Estimator now distinguishes site-total from in-solution counts. --solutionId arg queries solutioncomponents for the specific solution and returns componentCountSiteTotal + componentCountInSolution + orphansOnSite. Fixes the 908-on-site/361-in-solution ambiguity that misled plan reviewers. Live verified on SupplierInvoicePortal (6bcbd1a2-…) in org1e98cc97: siteTotal=908, inSolution=361, orphansOnSite=547. 2. plan-alm Solutions tab renders a Solution membership banner when the estimator has both counts. Calls out orphan count and links to sync-mode. 3. deploy-pipeline Phase 7.7 activation trigger broadened. Old trigger required the current solution to contain a website componentType=10374. That missed the real-world case where the site exists pre-existing on the target and the solution being shipped is tables+flows+bots only. New trigger: run whenever powerpages.config.json has a websiteRecordId (i.e. this is a Power Pages project). Informational secondary check on the solution contents remains. 4. New integration tests at scripts/tests/integration/ use a real HTTP mock server (http.createServer) so network code paths, Authorization header propagation, and @odata.nextLink pagination are exercised end-to-end. Covers discover-site-components.js with 4 scenarios. 330/335 plugin tests pass; alm-lint: 0 findings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * estimate-solution-size: stop double-counting table attributes as components Solution componentCount was inflated 2-3x on schema-heavy sites because the formula added schemaAttrCount (every table column). In Dataverse, a table with N columns is ONE solutioncomponents row of type 1 — attributes ride along, they're NOT individual components. Live evidence on SupplierInvoicePortal (org1e98cc97): Before: componentCount = 393 + 11 + 503 + 1 = 908 After: componentCount = 393 + 11 + 1 + 2 = 407 Actual in solution (solutioncomponents query): 361 Orphan delta = 46 (was falsely 547) New formula: ppcs + tables + envVars + cloudFlows (workflow entities, not the type-33 ppc binding which is already in ppcs). Bots and bot components remain a known undercount bound; they're captured by the solutioncomponents query (`inSolution.byComponentType`) when --solutionId is passed. No test changes — formula previously had no unit-test coverage asserting the number (only the fields existed). Added an inline comment mapping each term to its componenttype so the next contributor won't re-add attributes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * estimate-solution-size: add site-scoped bot + botcomponent queries Before: siteTotal formula skipped bots (type 10137) and bot components (type 10193) because they aren't reachable from /powerpagecomponents. Live data on SIP had 4 bots + 30 components in solution — none reflected in siteTotal — making orphansOnSite look artificially small (46 instead of 78). Root cause dig: type-27 "Bot Consumer" ppcs store the bot schema reference in the `content` JSON (`{"botschemaname": "..."}`), NOT in the `name` field (which is literally the string "Bot Consumer"). The first pass used `name` and silently returned zero bots. Now: fetch type-27 ppcs with content column, JSON.parse each, extract botschemaname, then query bots by schemaname to stay site-scoped (avoids folding env-wide bots from other projects into this site's total). For each discovered bot, query botcomponents filtered by parent bot id. Output gets two new fields: botCountScoped + botComponentCountScoped. Live re-verification on SupplierInvoicePortal (org1e98cc97): siteTotal: 407 → 439 (+2 bots + 30 bot components) inSolution: 361 (unchanged — from solutioncomponents query) orphansOnSite: 46 → 78 (more accurate — math: 82 ppc orphans + 1 env var − 2 bot consumer dupes in solution − 2 solution-only bots − 1 website) Real orphans on this site: 81 type-3 web files + 1 type-35 server logic + 1 env var = 83 solutionizable records. The 78 "net delta" reflects accounting offsets for rows that exist in solution but can't be represented in a site-scoped scan (bot consumer duplicate rows, website itself). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * estimate-solution-size: exclude Vite/Rollup bundle chunks + fix swapped picklist constants Two bugs fixed: 1. classifyPPCs used WEB_FILE=2 and WEB_PAGE=4, but the authoritative picklist (MS Learn + PPC_TYPE_LABELS in discover-site-components.js) says type 2 is Web Page and type 3 is Web File. On SIP, webFileCount was reporting 12 (actually Web Pages) while the site had 208 actual web files. Fixed to match the canonical enum; added the full ppc type lookup for completeness. 2. Bundle chunk noise excluded from both siteTotal and inSolution. `pac pages upload-code-site` leaves behind hash-suffixed .js/.css chunks every rebuild (Vite: Home-BPuZZDcA.js, index-DyzztwOp.js). They stay in Dataverse as powerpagecomponent rows linked to the site but aren't referenced by the current build's index.html. User feedback: "we do not care about random webfiles entries not actually associated with a pages site." Added isProbablyBundleChunk() heuristic (regex on hash-like suffix + .js/.mjs/.cjs/.css/.map extension); split classified.webFiles into webFiles (real content) + bundleChunks (noise). siteTotal now subtracts bundleChunks. inSolution subtracts the subset of bundleChunks that are in the solution via objectid intersection, so the comparison stays apples-to-apples. Live on SIP (org1e98cc97, SupplierInvoicePortal solution): before today after #1 after #2 (real) siteTotal 908 439 231 inSolution 361 361 233 orphans 547 (bogus) 78 0 (real) bundleChunkCount - - 208 (on site) chunksInSolution - - 128 Real orphans dropped to 0 because this solution is in fact in sync with the site's meaningful content; the earlier 82-orphan number was dominated by 81 Vite chunks that don't represent real missing content. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * estimate-solution-size tests: update to authoritative picklist + bundle-chunk heuristic Two test updates for today's picklist + bundle-chunk fixes: 1. classifyPPCs test now uses the authoritative powerpagecomponenttype picklist values (type 3 = Web File, type 2 = Web Page, type 8 = Web Template, type 11 = Web Role). Prior test asserted against swapped constants, which masked the bug that caused SIP's web file count to show 12 when the site actually held 208. 2. New test for the isProbablyBundleChunk heuristic. Covers 7 chunk name patterns (Vite hash suffixes, Webpack dot-separated hashes, sourcemaps with `.js.map`) + 7 real static asset names (hero.jpg, logo.svg, favicon.ico, Inter-Regular.woff2, app.js) to prove no false positives. Regex updated from `\.(?:js|mjs|cjs|css|map)$` to `\.(?:js|mjs|cjs|css)(?:\.map)?$` to correctly match `.js.map` sourcemaps whose penultimate segment is already an extension. All 8 estimate-solution-size tests pass. alm-lint: 0 findings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * countSolutionMembership: add cross-site safety check Defensive check for solutions that contain ppcs from a site other than the one we're planning. Rare in practice (usually people build one solution per site) but possible when someone manually stitches components across sites. Silent miscounting is worse than a 1-query cost, so wire it up. When estimator is called with both --solutionId and --websiteRecordId, pass the set of ppc ids from the site to countSolutionMembership. The helper filters solutioncomponents type-10373 rows by objectid, flagging any whose id isn't in the site set. Returns crossSitePpcs[] + the output object gets a human-readable crossSitePpcWarning or null. Live check on SupplierInvoicePortal (org1e98cc97): solutioncomponents type 10373: 311 rows Of those, in site 2bea4a71-...'s ppc set: 311 Cross-site miss: 0 warning: null ✓ (healthy) Separately confirmed that the 82 orphan ppcs on this site (81 Vite bundle chunks + 1 server logic "invoice-checker") all live in the well-known Active solution (fd140aae-4df4-11dd-bd17-0019b9312238) — i.e. they're in Dataverse's default working-copy bucket, not a different user solution. Test coverage: 2 new integration tests against the http-mock server cover (a) flagging a cross-site row and (b) skipping the check when sitePpcIdSet is null. 6/6 integration tests pass, 331/336 full suite (5 pre-existing render-alm-plan failures + 1 env-dependent auth test, unchanged). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * setup-solution: add Step 5.4c orphan ppc adoption (live-validated) Today's per-type breakdown on SupplierInvoicePortal exposed a gap not covered by the existing Step 5.4b: ppc rows (server logic, real pages, etc.) that live on the site but aren't in the user solution. invoice-checker server logic was the canonical case — created by /add-server-logic in an earlier session, never registered into the user solution, sat in Active. Adopted live today (AddSolutionComponent componenttype 10373); solution bumped 1.0.0.1 → 1.0.0.2. Step 5.4c mirrors 5.4b but for powerpagecomponent rows: - Calls discover-site-components.js for missing.powerpagecomponents - Partitions orphans: real-content vs. bundle-chunk (Vite/Rollup hash) - Bundle chunks are reported but NOT prompted — they're stale build artifacts, not real content - Real-content orphans deduped by (type, name) — older stale dupes (e.g. a second index.html from a superseded build) excluded from the adoption prompt - AskUserQuestion multiSelect for real orphans - AddSolutionComponent per selection with componentType: 10373 and AddRequiredComponents: false Documented API gotcha: DoNotIncludeSubcomponents: true returns HTTP 400 0x80040216 for non-Entity components. Leave it unset for type-10373. Also updated: - Sync-mode description to list 5.4c in the step sequence - Manifest tag list to include `[ADOPTED ppc — was in Active only]` - Phase 5.6 bulk-add list to include adopted orphan ppcs as entry #8 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * estimate-solution-size: address PR #119 review nits Three nits from the review agent, all addressed: 1. **Cloud-flow counting comment was misleading** — reviewer's concern was that `+ classified.cloudFlowLinks.length` appeared to double-count the type-33 ppc bindings already in ppcs.length. The code is actually correct (cloudFlowLinks.length serves as the workflow count since each type-33 binding pairs 1:1 with a type-29 workflow), but the comment didn't make that clear. Rewrote the block comment so future readers don't stumble on the same concern. 2. **Dropped `componentCount` alias, kept only `componentCountSiteTotal`** — per user: "we do not need backward compatibility since ALM skills are under active development on a feature branch." compute-split-plan.js now reads `estimate.componentCountSiteTotal` (11 call sites updated); compute-split-plan.test.js fixtures renamed too (10 call sites). render-alm-plan.js was already on the new name — no change needed. 3. **Bot pagination WARN on ceiling** — discoverBotsAndComponents emits a stderr WARN when bots.length hits BOTS_MAX_PAGES * BOTS_TOP (1,500) or botComponents.length hits COMPS_MAX_PAGES * COMPS_TOP (25,000). These caps are unlikely in any real tenant, but silent truncation is worse than a visible warning. Constants extracted so the ceiling is easy to bump in place. 333/338 tests pass; alm-lint: 0 findings. Live re-check against SupplierInvoicePortal org1e98cc97: siteTotal: 231 inSolution: 234 (+1 from earlier 233 — invoice-checker adopted) orphans: 0 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * estimate-solution-size: stop subtracting bundle chunks from inSolution.total Live validation against SupplierInvoicePortal in dev surfaced that we were under-counting in-solution components by 128 (the bundle-chunk ppcs). Bundle chunks that were added to the solution via AddSolutionComponent ARE legitimate members — they ship with the managed export — so stripping them from the in-solution count produced numbers that didn't match the Maker UI. Power Platform UI shows SIP's solution at 362 (= 311 site components + 11 tables + 1 site record + 1 site language + 4 connection references + 2 cloud flows + 2 agents + 30 agent components). Estimator now matches exactly. Changes: - inSolution.total: raw row count, no bundle-chunk subtraction. - componentCountSiteTotal: raw site inventory (bundle chunks included). - componentCountSiteActionable: new sub-count that strips bundle chunks for the "actionable content" view. - orphansOnSite: computed from the actionable count so stale upload artifacts don't get flagged as content gaps. - Component-type comment block rewritten with the authoritative solutioncomponents enum (10137=Connection Reference, 10192=Bot, 10193=Bot Component). Earlier comments had these swapped. - render-alm-plan: headline count prefers componentCountInSolution when available (that's what the pipeline ships); falls back to siteTotal. - Membership banner wording clarified — raw rows vs. owned components, "actionable orphan" terminology. All 40 targeted tests pass. The 5 pre-existing render-alm-plan test failures (approval-gate / env-var / solutionContents scenarios) are unchanged — unrelated to this commit and present on the baseline too. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * estimate-solution-size: note that solutioncomponent types are dynamic per tenant Two nits from review: 1) The componenttype block I rewrote yesterday presented 1/29/380/10137/10192/ 10193/10373/10374 as if they were stable constants. They are not — the Dataverse solutioncomponent.componenttype picklist is dynamic per tenant and AddSolutionComponent callers are expected to resolve values at runtime via scripts/lib/discover-component-types.js. (This is already documented in MEMORY.md but my rewrite didn't reflect it.) countSolutionMembership() in this file was already resolver-free and does the right thing (tallies whatever values Dataverse returns). Only the comment block needed to stop implying the integers are universal. 2) Added a concrete derivation of how siteTotalComponents evaluates to 439 for the live SIP reference site, plus the note that connection references (4) and the website record itself (1) are not included — that's why the diff against inSolution is ~77 rather than exactly matching the 82 orphans originally reported. Also fixed the bot-discovery doc block: same "dynamic, not static" caveat plus pointer to discover-component-types.js for mutation paths. 40/40 targeted tests still pass. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Power Pages ALM: per-skill telemetry on 6 skills + 5 plan-alm UX fixes from first review (#126) * skill-tracking: add to 6 ALM skills + extend reference table Every Power Pages skill must record usage via update-skill-tracking.js in its final phase (per plugins/power-pages/CLAUDE.md convention). Six ALM skills were missing the tracking call: - setup-solution (SetupSolution) - export-solution (ExportSolution) - import-solution (ImportSolution) - hotfix-solution (HotfixSolution) - diagnose-deployment (DiagnoseDeployment) - configure-env-variables (ConfigureEnvVariables) Each now has a "Record Skill Usage" sub-step at the end of its final phase using the pointer pattern (> Reference: ...skill-tracking-reference.md). references/skill-tracking-reference.md: added the 8 missing PascalCase mappings (the 6 above plus setup-pipeline / deploy-pipeline, which already had the invocation inline but were absent from the table). Coverage: 23 / 24 power-pages SKILL.md files now record usage; only the shared report-issue wrapper is intentionally excluded (cross-plugin shared skill that does not create site artifacts). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * plan-alm: 5 UX fixes from first end-to-end review 1. Site identity: prefer .powerpages-site/website.yml (source of truth when run from a site root) over powerpages.config.json. The config file is kept only as a fallback for plugin-dev scenarios. Reads `id` → websiteRecordId and `adx_name` → siteName from the YAML. 2. Plan presentation: render docs/alm-plan.html AND open it in the default browser BEFORE prompting for approval. Previously the HTML was rendered but users had to approve based on the inline CLI summary alone; they couldn't see the full plan (size gauge, signal cards, per-solution breakdown) until after approving. Cross-platform launch via Node.js — silent + non-blocking. 3. Size gauge: drop the inline fill label when fillPct < 15%. For small sites (e.g. 4.9 MB / 95 MB ≈ 4% fill), the "4.9 MB" label inside the narrow pill overflowed and rendered as a floating chip that read like a different number. The headline size-gauge-value already shows the exact MB on the right, so no information is lost. 4. Remove the "Scale data — what we've tested" block from the Size tab. Static reference numbers (1k-2k typical, 6k tested, 10+ hour extreme case) weren't actionable for reviewers assessing THEIR plan; the tier classification on signal cards already conveys where they are. 5. Approved-by capture: Phase 4 options 1 and 2 now capture the approver via `git config user.name` (fallback to USER/USERNAME env var, then AskUserQuestion), and stamp both `<span id="approved-by">` and `<span id="approval-date">` in the HTML. Previously Approved-by was left blank in the audit trail. Test suite: 14 pass / 5 fail — same counts as before these edits (failures pre-existed and are unrelated to this change). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * plan-alm: delete samples/ folder + harden browser-open to PowerShell Samples folder removal: The plugins/power-pages/skills/plan-alm/samples/ folder shipped synthetic IdeaSphere fixtures and a pre-rendered demo HTML (~75 KB total). Nothing in the repo referenced it — grep across scripts/tests/ came back empty. It was design scaffolding, not regression-protected, and actively misleading after today's template changes (the pre-rendered HTML still contained the old "Scale data" block + the broken size-gauge label). Real demos should use live projects (e.g. C:\Projects\supplier-invoice-portal) where docs/alm-plan.html is always current. Browser-open command hardening (Gap 2 follow-up): First pass used `cmd /c start "" <path>` via Node spawn with detached:true. That command exits 0 on sandboxed terminal hosts without actually launching a browser — the GUI spawn gets suppressed silently. Switched to platform-specific launchers that respect file associations and are resilient: - Windows: `powershell.exe Start-Process` (reliable regardless of parent shell) - macOS: `open <path>` - Linux: `xdg-open <path>` Also print a file:// URL alongside the spawn so if the auto-open fails (headless environment, SSH session, restricted terminal), the user has a direct clickable link. Updated the user-facing message to include the URL as a fallback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ensure-pipelines-host skill + plan-alm UX overhaul + 3-entity site model fix (#134) * ensure-pipelines-host: Slice A — detection scripts + plan SKILL.md 5 new helpers under scripts/lib + 57 unit tests + plan-only SKILL.md: - check-env-host-binding.js — POST GetOrgDbOrgSetting('ProjectHostEnvironmentId') - resolve-env-by-id.js — BAP env GET with $expand; 404-ambiguous handling - list-tenant-envs.js — tenant-wide enum + per-env Pipelines probe (Production sku default, name-hint+admin-perms ranking, --firstHitWins, --includeName) - verify-host-readiness.js — WhoAmI + solutions filter (covers both presence and version capture in one query) - ensure-pipelines-host-detect.js — orchestrator wrapper that runs cache fast-path + Phase 2 resolution order + Phase 5 verify; emits .last-host-check.json schema with actionTaken: "none" Resolution order mirrors ProjectHostProvider.tsx; produces 8 status values including AvailableUsingPlatformHost / AvailableUnboundCustomHost / MultipleUnboundCustomHosts / PlatformHostExistsUnbound / CannotRedirect. Live-validated against SIP (org1e98cc97 → pascalepipelineshost): resolutionStatus: AvailableUsingCustomHost pipelinesSolutionVersion: 9.1.2026034.260325188 ready: true (7.8s first run, 5.4s on cache hit) Recon findings recorded in SKILL.md Open Items 1, 3, 10: - Pipelines solution uniquename = msdyn_AppDeploymentAnchor (live-confirmed) - BAP env-list never returns linkedEnvironmentMetadata.templates (0/1000 envs) - deploymentpipelines?$top=0 returns 400 — switched to solutions filter Slice B (provision-custom-host + SKILL.md execution wiring) and Slice C (setup-pipeline delegation) follow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ensure-pipelines-host: Slice B (provisioning + validator) + Slice C (setup-pipeline delegation) Slice B — Custom Host fast-path provisioning: - provision-custom-host.js — POSTs BAP env-create with the D365_ProjectHost template (eng.ms-confirmed: same template PPAC's New custom host button uses internally). Polls the lifecycle op URL with Retry-After-aware backoff; handles multiple response shapes (properties.provisioningState / state / status.code); 5xx-transient retries; 401/403 with explicit guidance. Falls back to a direct env GET when the lifecycle op response lacks linkedEnvironmentMetadata. Test suite covers 14 scenarios including timeout, Failed terminal state, transient 5xx, and synthetic-clock timeout. - skills/ensure-pipelines-host/scripts/validate-ensure-host.js — Stop-hook validator. Accepts schemaVersion 1 or 2; treats CannotRedirect / OrgSettingStale / PermissionDenied as documented terminal-error states (skill ran successfully even though host isn't usable). Blocks on missing required fields, unknown resolutionStatus, ready=false on non-terminal status, or ready=true with finalHostEnvUrl missing. 15 tests. - powerpages-hook-utils.js — registers ensure-pipelines-host in TRACKED_SKILLS so the central PostToolUse hook routes the validator on skill stop. - ensure-pipelines-host/SKILL.md — drops PLAN-ONLY banner; scope note now reads as the executable contract. Slice C — setup-pipeline delegates to ensure-pipelines-host: - skills/setup-pipeline/SKILL.md Phase 1 step 4 rewritten. The old discover-pipelines-host-only call (which only checked tenant-default-custom- host) is replaced with ensure-pipelines-host-detect.js — the orchestrator wrapper that runs the full resolution order. Branches on resolutionStatus: AvailableUsing* → use directly, continue *Unbound* / NoHost → delegate to /power-pages:ensure-pipelines-host CannotRedirect / → stop with admin-resolution guidance OrgSettingStale / PermissionDenied Phase 3 host-confirmation prompt updated — no more "NOT DETECTED — please provide" fallback; HOST_ENV_URL is always populated by Phase 1 (or the skill stops before reaching Phase 3). Cumulative test count: 132 (5 Slice A test files + 2 Slice B test files + existing regression suites: discover-pipelines-host, validate-deploy-pipeline, powerpages-hook-utils, lint-skills-alm). Live-validated against SIP (org1e98cc97): ensure-pipelines-host-detect.js → marker → validate-ensure-host.js → exit 0 Missing marker → validate-ensure-host.js → exit 0 (graceful) NOT YET LIVE-TESTED: provision-custom-host.js end-to-end (would create a real Custom Host env in tenant — needs explicit user authorization before that acceptance test). Unit tests cover all branch paths. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * plan-alm host integration + ensure-pipelines-host SKILL polish plan-alm — wires the new ensure-pipelines-host detection into the orchestrator so the generated docs/alm-plan.html reflects host state before the user approves the plan. - SKILL.md Phase 1 step 12 (NEW): runs ensure-pipelines-host-detect.js after the completeness check; acquires BAP_TOKEN; tmp-file-then-mv pattern; skips when PIPELINE_DONE=true; on detect failure sets HOST_RESOLUTION = { status: 'DetectionFailed' } and continues. Maps wrapper output (resolutionStatus / finalHostEnvUrl / isPlatformHost) into the plan-alm HOST_RESOLUTION shape with hostType = isPlatformHost ? 'platform' : (finalHostEnvUrl ? 'custom' : null). - SKILL.md Phase 2 Q4 (REWRITTEN): 7-row branch table on HOST_RESOLUTION.status. AvailableUsing*/Unbound*/PlatformHost* populate HOST_ENV_URL and set WILL_ENSURE_HOST / WILL_PROVISION_CUSTOM flags as appropriate. CannotRedirect blocks the skill. OrgSettingStale / PermissionDenied / DetectionFailed fall back to manual entry with pac env list pre-fill. - SKILL.md Phase 3 planData: new hostResolution block with the 9 spec fields. Documented field-by-field mapping inline so the orchestrator can populate it deterministically. - SKILL.md Phase 3 risks: 5 new entries (NoHost, AvailableUnboundCustomHost, MultipleUnboundCustomHosts, PlatformHostExistsUnbound info-level; CannotRedirect warning). - SKILL.md Phase 4 inline summary: adds "Pipeline host: {url} ({status})" line, with "Will be ensured during setup-pipeline" variant when willEnsureDuringExecution: true. - render-alm-plan.js: buildHostCardHtml emits three modes (host-card-ok green for AvailableUsing*, host-card-pending yellow for willEnsureDuringExecution, host-card-blocked red for CannotRedirect). buildHostChecklistSubBullet emits an indented substep under the Setup pipeline checklist item only when the host will be ensured at execution time. Both wired into the replacements map. - alm-plan-template.html: __PIPELINES_HOST_CARD__ placeholder above the existing pipeline stages diagram; __HOST_CHECKLIST_SUBSTEP__ placeholder after __CHECKLIST_HTML__. CSS for .host-card-ok / -pending / -blocked plus .checklist-substep-list / .checklist-substep / .substep-note. - render-alm-plan.test.js: +7 tests covering all three card modes, MultipleUnboundCustomHosts candidates count, no-render when hostResolution is absent, substep render path, substep omission when host already established. 26 tests / 21 pass / 5 fail — same 5 baseline failures as HEAD (pre-existing, unrelated to this work; verified via git stash baseline run). Spec ambiguities resolved (worth review): - DetectionFailed not in spec's 9-status list but spec says use it on non-zero exit. Added to status enum. - Renderer emits checklist items as <div class="checklist-item">, not <li> — substep wrapped in <ul class="checklist-substep-list"> so it's valid HTML when slotted directly into the template. - Used --high-border for pending color (--warning-border doesn't exist in the palette). ensure-pipelines-host SKILL.md polish: - Drops "Scripts to be Implemented" stale header → "Scripts" with all 6 helpers as shipped (table now reflects --includeName, --firstHitWins, --maxEnvsToProbe defaults, ensure-pipelines-host-detect.js wrapper row). - Integration section: setup-pipeline and plan-alm both marked ✅ wired with concrete invocation snippets and behavior summaries. - Phase 1.0 cache fast-path probe corrected from deploymentpipelines?$top=0 → solutions?$filter=uniquename eq 'msdyn_AppDeploymentAnchor'&$top=1 (cheap-probe correction validated against pascalepipelineshost 2026-04-28; deploymentpipelines?$top=0 returns 400 even on a working host). Threat-model and progress-table rows updated to match. - Validation script section: registration mechanism corrected from hooks/hooks.json to TRACKED_SKILLS in scripts/lib/powerpages-hook-utils.js (the actual mechanism after Slice B added the skill there). Live-validated end-to-end: Smoke render with synthetic AvailableUnboundCustomHost data → host-card-pending div present (4 hits incl CSS), checklist-substep present (4 hits incl CSS), HTML well-formed. Cumulative test count: 428 (423 pass + 5 pre-existing failures, all in render-alm-plan.test.js, none introduced by this work). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ensure-pipelines-host: PAC-CLI shim for BAP — works in tenants where Az→BAP fails Adds --source pac|bap|auto (default auto) to the detection scripts so they work in tenants where BAP rejects Az-CLI-acquired tokens with 401 InvalidAuthenticationToken (verified 2026-04-28 against D365DemoTSCE53051106 demo tenant — token claims show correct user/tenant/ audience but BAP still rejects; PAC CLI works for the same user because it uses a different first-party client ID with implicit BAP grants). New file: - scripts/lib/pac-bap-shim.js — wraps `pac admin list --json` into BAP-shaped env list. Maps PAC fields to BAP fields (EnvironmentId → name, Type → environmentSku, EnvironmentUrl → instanceUrl, etc.). Derives instanceApiUrl by inserting ".api" before crmN.dynamics.com. Fields not provided by PAC (tenantId, lastModifiedTime, permissions, isManaged) returned as null — none are critical for host detection. PAC doesn't list Platform-sku envs, so PE detection still requires source=bap with a working BAP token. Updated scripts: - list-tenant-envs.js — adds --source flag. auto (default): try BAP first, fall back to PAC on 401/403. pac: skip BAP entirely. bap: existing behavior. Output adds sourceUsed + fallbackReason. - resolve-env-by-id.js — same flag pattern. PAC mode filters the full list by EnvironmentId. On PAC-not-listed returns { found: false, reason: "not-in-pac-list" }. - ensure-pipelines-host-detect.js — propagates --source through to the helpers. --bapToken now optional in auto/pac modes. Tests: - pac-bap-shim.test.js (NEW) — 20 tests covering field mapping, URL derivation, JSON parsing edge cases (header prose, malformed JSON, exec errors). - Updated existing tests for the new auto-mode semantics: tests that previously expected "throws when bapToken missing" now use --source bap explicitly. - New tests for source=pac, source=auto with 401 fallback to PAC, surfaces original error when both fail. Live-validated against new tenant (admin@D365DemoTSCE53051106): - --source pac: detected Stage-1 + Stage-2 as eligibleForAppInstall in 14s, 0 BAP calls. - --source auto with rejected BAP token: BAP returns 401, falls back to PAC, returns same result in 7.4s. Cumulative test count: 186 (181 pass, 5 pre-existing render-alm-plan failures unrelated to this work). +28 net new tests in this commit. Auth strategy section added to SKILL.md documenting the PAC-first-with- BAP-fallback approach. Provisioning (provision-custom-host.js for D365_ProjectHost env-create) still requires BAP — that's the one path where Az tokens are needed; documented explicitly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PP Pipelines helpers: fix unprefixed schema (msdyn_ format never shipped) The two PP Pipelines API helpers used msdyn_-prefixed property names in their POST bodies (e.g., msdyn_name, msdyn_url, msdyn_type, msdyn_pipelineid @odata.bind, msdyn_targetenvironmentid@odata.bind). Live testing on 2026-04-28 against the shipped Pipelines version (msdyn_AppDeploymentAnchor v9.1.2026034.260325188 in the D365DemoTSCE53051106 demo tenant) revealed that the actual deploymentenvironment / deploymentpipeline / deploymentstage entities use **unprefixed** field names — the msdyn_ prefix is rejected ("Invalid property 'msdyn_name'"). Per references/power-pipeline-skill-reference.md, the unprefixed schema is the canonical format. The helpers had diverged from the spec — likely authored from early-preview HARs that used msdyn_ before ship. Fixed: - create-deployment-environment.js: msdyn_name/msdyn_url/msdyn_type → name/environmentid/environmenttype. The …
* feat(security-review): add HTML report generation and supporting documentation - Introduced a new HTML report template for security reviews, providing a structured overview of findings, scores, and recommendations. - Created a script (`render-review.js`) to render the HTML report from JSON data files. - Added documentation for the seven-step conversation flow used in the security review process. - Included a glossary of technical terms relevant to the security review findings. - Defined the data format for sections and findings to ensure consistency in report generation. * feat(security-review): enhance security review flow and parallelize sub-skill execution * Refactor manage-code-scan and manage-site-scan skills for clarity and consistency - Updated terminology in SKILL.md files to replace "quick/deep" with "basic/advanced" for better understanding. - Adjusted command parameters in commands.md to reflect new terminology. - Removed get-security-score.js script as its functionality is no longer needed. - Simplified start-deep-scan.js to eliminate unnecessary username/password parameters. - Enhanced review-security skill to streamline user interactions and improve clarity in reporting. - Updated HTML report to remove score display and adjust layout for better readability. - Revised flow.md and section-data-format.md to align with the latest changes in skill behavior and reporting structure. * fix(security-review): update terminology for clarity in security review steps * refactor(manage-site-scan): update descriptions and remove quick scan script for clarity and consistency * feat(security-review): add HTML report generation and supporting documentation - Introduced a new HTML report template for the security review skill, providing a structured overview of findings, summaries, and next steps. - Created a script to render the HTML report from JSON data, ensuring required keys are validated. - Added documentation for the seven-step conversation flow used in the security review process. - Included a glossary of technical terms relevant to the security review findings. - Defined the data format for sections and findings to standardize report generation. * Add scan-site skill and related scripts for Power Pages security scanning - Implemented `scan-site` skill to perform security scans on deployed Power Pages sites. - Created scripts for starting deep scans, polling scan status, and fetching latest reports. - Added command reference documentation for the new scan-site functionality. - Updated existing security review skill to integrate with the new scan-site capabilities. - Introduced `run-trivy.js` script for scanning code dependencies and vulnerabilities. * refactor(skills): remove progress tracking tables from manage-firewall, manage-headers, and scan-site skills for clarity * refactor(manage-firewall, manage-headers, scan-code, scan-site, security-review): standardize JSON formatting and improve clarity in user prompts * refactor(manage-firewall): enforce rule naming conventions and update examples for clarity * Refactor firewall management scripts and documentation - Deleted outdated rule cookbook documentation and replaced it with a new rule reference document that provides a comprehensive guide on creating and managing firewall rules. - Updated scripts for deleting, enabling, disabling, and retrieving firewall rules to use the Power Platform API instead of the Admin API. - Enhanced error handling and output formatting in the get-rules and get-status scripts. - Modified set-rules script to accept inline JSON data for rule creation and updates, improving usability. - Added timeout settings for rule deletion and creation to accommodate longer processing times. * Refactor scan-site scripts to use Power Platform API; update command documentation and add scan reference schema - Changed admin-api references to power-platform-api in scripts: get-latest-report.js, poll-deep-scan.js, start-deep-scan.js. - Updated command documentation to reflect changes in output and usage. - Added scan-reference.md for detailed schema of scan report and alert normalization. - Improved error handling and response structures in get-latest-report.js and poll-deep-scan.js. * Add headers reference documentation and remove obsolete scripts - Created a new documentation file for headers reference detailing site settings and response headers in Power Pages. - Removed the `inspect-headers.js` script as it is no longer needed. - Removed the `scan-external-urls.js` script due to redundancy. - Updated the `SKILL.md` and `commands.md` files to clarify rate limits and scan statuses. - Adjusted severity mapping in `scan-reference.md` to align with API values. * refactor(manage-headers): enhance security recommendations and clarify CSP directives * refactor(manage-headers): update skill tracking instructions for clarity and brevity * Refactor scan-code commands and tool installation documentation - Simplified command descriptions and usage for `check-tools.js`, `run-opengrep.js`, and `run-trivy.js`. - Updated output format to return JSON directly to stdout instead of writing to files. - Removed the tool installation guide as a separate document; now users are instructed to install tools based on `check-tools.js` output. - Enhanced error handling and timeout settings in `check-tools.js`, `run-opengrep.js`, and `run-trivy.js`. - Adjusted the security review skill documentation to reflect changes in workflow and terminology. * refactor(scan-code): update scan depth options and enhance trivy command flags * refactor(security-review): improve clarity and consistency in documentation and remove obsolete glossary * fix(scan-code): clarify description for Basic risk coverage in user prompts * Refactor security review and scan report scripts - Added a new script `render-review.js` to generate security review HTML reports from JSON data. - Removed the obsolete `render-scan-report.js` script. - Updated documentation in `manage-firewall` skill to reflect changes in JSON structure and usage. - Standardized example usage across various scripts to replace `<guid>` with `<portal-id>`. - Enhanced `run-opengrep.js` and `run-trivy.js` to output raw JSON directly, removing unnecessary parsing. - Deleted the `section-data-format.md` reference file as it was no longer needed. - Removed the old `render-review.js` script from the security review skill, consolidating functionality into the new script. * Enhance scan-code and scan-site skills with unified JSON output and transformation scripts - Updated scan-code skill to save raw JSON outputs from opengrep and trivy to temporary files, and introduced a new transform script to normalize findings into a unified format. - Revised documentation for scan-code to reflect changes in output handling and normalization process. - Added transform-report script for scan-site to convert deep-scan reports into a unified findings shape, including detailed severity mapping. - Improved scan-site skill documentation to clarify the fetching and transformation of reports, including handling of empty report cases. - Updated security-review skill to ensure findings are processed verbatim from transform scripts, with clear policies on severity and annotations for inventory sections. * fix: update usage examples to replace <guid> with <portal-id> in scripts and documentation * Refactor scan-code functionality: remove scripts and documentation - Deleted the following scripts: check-tools.js, run-opengrep.js, run-trivy.js, transform-scan-code.js. - Removed associated documentation from commands.md. - Updated scan-site and security-review skills documentation to reflect changes in functionality and clarify usage. - Adjusted the flow of the security-review skill to streamline the conversation steps and improve user experience. * feat(security-review): integrate scan-code skill for local source and package vulnerability checks - Added scan-code skill to scan local Power Pages projects for dependency vulnerabilities and code issues using npm audit and ESLint. - Updated security-review skill to incorporate scan-code findings alongside live-site checks. - Enhanced HTML report generation to include separate sections for package and code findings with verbatim severities. - Modified existing styles and severity labels in the security review report template to accommodate new severities. - Documented the scan-code skill with detailed usage instructions and workflow. * Revert "feat(security-review): integrate scan-code skill for local source and package vulnerability checks" This reverts commit 4c87aca. * feat(security-review): enhance security review skill with improved flow and error handling * Refactor scan-site and security-review skills for improved user experience and code clarity - Updated SKILL.md for scan-site to streamline user options and remove unnecessary confirmation steps before scanning. - Enhanced get-latest-report.js to improve error handling and modularize the main function. - Refactored poll-deep-scan.js to introduce better timeout handling and improve readability. - Simplified start-deep-scan.js by consolidating argument parsing and error handling. - Transformed transform-report.js to improve report parsing and error messaging, ensuring better handling of malformed reports. - Revised security-review SKILL.md to reduce phases from seven to six, clarifying workflow and user interactions. - Updated flow.md to reflect changes in the security-review skill phases and improve clarity on user-facing beats. * test(power-pages): address Copilot PR microsoft#151 review comments Reconciles three doc inconsistencies and adds node:test coverage for the four scripts Copilot flagged as untested. Doc fixes: - security-review SKILL.md § 3.1.2: skipped subagent placeholders are now rendered without a severity field, matching the no-severity policy in § 3.1.3. build-review-data.js's skippedSection() drops severity:'info' and the totals.info bump. - scan-site SKILL.md § 5.1 + transform-report.js: the empty status now emits a single info finding (via new emptyReport() helper) rather than an empty findings array, so review-mode output never produces an empty section. - security-review SKILL.md Constraints: the template reference now points at scripts/lib/templates/security-review-report.html (the real location) instead of the non-existent assets/. New tests under scripts/tests/ (64 cases total, all passing): - build-review-data.test.js: SECTION_MAP shape, SEVERITIES order, buildSections behaviour (skipped without severity, totals aggregation, unknown severities, empty dir, JSON-parse failures), TZ-pinned formatGeneratedAt, and CLI surface (--help, missing flags, --inputDir validation, --nextStepsFile folding). - render-review.test.js: missing --output/--data exits, missing data file, unparseable JSON, parameterised required-key validation over all 6 keys (asserts stderr names the missing key), and end-to-end render that verifies REPORT_NAME/SITE_NAME/ REVIEW_DATA-derived values reach the rendered HTML. - power-platform-api.test.js: parseCliArgs, parseTimeoutMs (with parameterised rejection of 'abc'/'0'/'-1'/'-0.5'/'NaN'/''), hasErrorCode is status-agnostic, isFeatureUnsupported variants, pollUntil happy/timeout/error paths plus a stub-clock test that pins DEFAULT_POLL_INTERVAL_MS as the wired-up default and another that asserts a caller-supplied intervalMs flows through. - website.test.js: nextSkipFrom and recordIdOf edge cases plus findWebsite pagination via @odata.nextLink, case-insensitive match, non-advancing-server guard, request/context errors, and empty-websiteId rejection. Uses require.cache injection to stub the api module — documented Node API, restored in finally. Full plugin suite: 984 tests pass, 0 fail. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Ramachandran R <ramacr@microsoft.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Document the latest Power Pages skills in README.md - Add ALM/CI-CD and security skill descriptions - Require future user-invocable skills to be documented Co-authored-by: GitHub Copilot <copilot@github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upstream Sync
Merges 3 upstream commits from
microsoft/power-platform-skillsinto this Equinor fork.Upstream Commit Range
3b2009f..2923950 (upstream/main)
Commits Included
2923950e55e1d32c82e39Scope
plugins/power-pages/Key Additions
scripts/lib/for ALM automationMerge Strategy
docs/equinor-alignment/content untouched.Review Record
The existing
power-pages.jsonreview record (dated 2026-05-27) already documents these upstream additions and passes schema validation. Status remainsdeferpending detailed security review of the expanded surface area.Validation
node scripts/validate-plugin-reviews.js— all 5 review records pass ✓Owner Decision Required