Claude-skill-registry dataverse-deploy

Deploy solutions, PCF controls, and web resources to Dataverse using PAC CLI

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dataverse-deploy" ~/.claude/skills/majiayu000-claude-skill-registry-dataverse-deploy && rm -rf "$T"
manifest: skills/data/dataverse-deploy/SKILL.md
source content

Dataverse Deploy

Category: Operations Last Updated: January 19, 2026 Primary Guide:

docs/guides/PCF-DEPLOYMENT-GUIDE.md


Critical Rules

These rules are MANDATORY. See PCF-DEPLOYMENT-GUIDE.md for full details.

MUST:

  • MUST use unmanaged solution unless explicitly told to use managed (ADR-022)
  • MUST use Dataverse publisher
    Spaarke
    with prefix
    sprk_
  • MUST rebuild fresh every deployment (
    npm run build:prod
    )
  • MUST copy ALL 3 files to Solution folder (bundle.js, ControlManifest.xml, styles.css)
  • MUST update version in ALL 5 locations
  • MUST include
    .js
    and
    .css
    entries in
    [Content_Types].xml
  • MUST use
    pack.ps1
    script (creates forward slashes in ZIP)
  • MUST disable/restore CPM around PAC commands

🚨 CRITICAL - Control Version is the Cache Key:

  • MUST update
    ControlManifest.Input.xml
    version FIRST - this is what Dataverse uses to detect updates
  • MUST increment the control version for EVERY deployment, not just the solution version
  • ⚠️ If you only update
    solution.xml
    but not
    ControlManifest.Input.xml
    , Dataverse will NOT update the control

NEVER:

  • NEVER use managed solution unless explicitly told - unmanaged is the default
  • NEVER use or create a new publisher - always use
    Spaarke
    (
    sprk_
    )
  • NEVER reuse old solution ZIPs - always pack fresh
  • NEVER use
    Compress-Archive
    - creates backslashes, breaks import
  • NEVER skip copying files - stale bundles cause silent failures

⚠️ AVOID (but valid fallback):

  • ⚠️ AVOID
    pac pcf push
    for production - rebuilds in dev mode, larger bundles
  • BUT use
    pac pcf push --publisher-prefix sprk
    when solution imports empty (see Troubleshooting)

Deployment Workflow

Follow PCF-DEPLOYMENT-GUIDE.md for complete details.

Step 1: Build Fresh

cd src/client/pcf/{ControlName}
rm -rf out/ bin/
npm run build:prod

# Verify size (~200-400KB, NOT 8MB)
ls -la out/controls/control/bundle.js

Step 2: Update Version (5 Locations)

#FileUpdate
1
control/ControlManifest.Input.xml
version="X.Y.Z"
2
control/{Component}.tsx
UI version footer
3
Solution/solution.xml
<Version>X.Y.Z</Version>
4
Solution/Controls/.../ControlManifest.xml
version="X.Y.Z"
5
Solution/pack.ps1
$version = "X.Y.Z"

Step 3: Copy Fresh Build to Solution

cp out/controls/control/bundle.js \
   out/controls/control/ControlManifest.xml \
   out/controls/control/styles.css \
   Solution/Controls/sprk_Spaarke.Controls.{ControlName}/

Step 4: Pack and Import

# Disable CPM
mv /c/code_files/spaarke-wt-ai-rag-pipeline/Directory.Packages.props{,.disabled}

# Pack (creates fresh ZIP with forward slashes)
cd Solution && powershell -ExecutionPolicy Bypass -File pack.ps1

# Import
pac solution import --path bin/{SolutionName}_vX.Y.Z.zip --publish-changes

# Restore CPM
mv /c/code_files/spaarke-wt-ai-rag-pipeline/Directory.Packages.props{.disabled,}

Step 5: Verify

pac solution list | grep -i "{SolutionName}"

Hard refresh browser (

Ctrl+Shift+R
) and verify version footer.

⚠️ Why avoid

pac pcf push
for production? It rebuilds in development mode, ignoring production optimizations. Bundle size increases from ~300KB to 8MB. Tree-shaking is lost. However, it is a valid fallback when
pac solution pack
imports an empty solution (see "Solution Imports But Is Empty" in Troubleshooting).


🚨 Dataverse Control Caching (READ THIS)

Dataverse caches PCF controls aggressively. If your deployment seems to succeed but the browser still shows old behavior, the cache wasn't busted.

Root Cause

Dataverse determines whether to update a control based on the control manifest version (

ControlManifest.Input.xml
), NOT just the solution version. If you:

  • ✅ Update
    solution.xml
    version
  • ❌ Forget to update
    ControlManifest.Input.xml
    version

...then Dataverse sees the same control version and silently keeps the old bundle.

The Fix: Version Order Matters

Always update versions in this order:

  1. FIRST:
    control/ControlManifest.Input.xml
    version="X.Y.Z"
    (THIS IS THE KEY)
  2. Then rebuild (this propagates to
    out/controls/control/ControlManifest.xml
    )
  3. Copy all 3 files to Solution folder
  4. Update
    Solution/solution.xml
    <Version>X.Y.Z</Version>
  5. Update
    Solution/pack.ps1
    $version = "X.Y.Z"

Nuclear Option: Delete and Reimport

If you've deployed but the control is still cached:

# Delete the solution completely from Dataverse
pac solution delete --solution-name {SolutionName}

# Reimport fresh
pac solution import --path bin/{SolutionName}_vX.Y.Z.zip --publish-changes

# Verify
pac solution list | grep -i "{SolutionName}"

Then hard refresh browser (

Ctrl+Shift+R
).

Symptoms of Caching Issue

SymptomLikely Cause
pac solution import
succeeds but UI unchanged
Control version not incremented
pac solution list
shows new version but old behavior
Control manifest version mismatch
Hard refresh doesn't helpNeed delete + reimport
Same control works in one browser but not anotherBrowser cache - try incognito

Decision Tree: Which Workflow?

Is the PCF embedded in a Custom Page?
├── YES → See "Custom Page Deployment" section in guide
└── NO → Use "Deployment Workflow" above (build → copy → pack.ps1 → import)

Primary Guide:

docs/guides/PCF-DEPLOYMENT-GUIDE.md
- Complete workflow with version management and troubleshooting.


Purpose

Deploy Dataverse components using PAC CLI following the PCF-DEPLOYMENT-GUIDE.md. This skill handles authentication, Central Package Management conflicts, and solution import issues.


Best Practices

PracticeImplementation
Always use unmanagedNever export/pack as managed unless user explicitly requests
Always use Spaarke publisherNever create a new publisher - use
Spaarke
(
sprk_
)
Always build freshRun
npm run build:prod
, never reuse old artifacts
Always use pack.ps1Never use
Compress-Archive
(creates backslashes)
Version footerEvery PCF MUST display
vX.Y.Z • Built YYYY-MM-DD
in the UI
Version bumpingIncrement version in ALL 5 locations
Verify deploymentALWAYS run
pac solution list
after import to confirm version
Use React 16 APIsDataverse provides React 16.14.0 - see ADR-022

Key Guidance

  • Prefer pack.ps1 workflow for production - build → copy files → pack.ps1 → import
  • Use
    pac pcf push
    as fallback
    when solution imports empty (Customizations.xml issue)
  • Version Locations: Update ALL 5: (1) ControlManifest.Input.xml, (2) UI footer, (3) Solution.xml, (4) extracted ControlManifest.xml, (5) pack.ps1
  • Full Guide: See
    docs/guides/PCF-DEPLOYMENT-GUIDE.md
    for complete workflow

Prerequisites Check

Before ANY deployment operation:

# 1. Check PAC CLI is installed
pac --version

# 2. Check authentication status
pac auth list

# 3. Verify active connection points to correct environment
# Look for: Active = *, Environment URL matches expected target

Expected Output

Index Active Kind      Name         Cloud  Type Environment   Environment Url
[1]          UNIVERSAL      dev          Public User HIPC DEV 2    https://hipc-dev-2.crm.dynamics.com/
[2]   *      UNIVERSAL      prod         Public User SPAARKE DEV 1 https://spaarkedev1.crm.dynamics.com/

If No Active Auth

# Create new authentication
pac auth create --environment "https://YOUR-ENV.crm.dynamics.com"

# Or select existing profile
pac auth select --index 1

Additional Scenarios

Scenario 1: PCF Control Deployment

Use the "Deployment Workflow" section above. Follow PCF-DEPLOYMENT-GUIDE.md for the complete workflow:

  1. Build fresh (
    npm run build:prod
    )
  2. Update version in ALL 5 locations
  3. Copy ALL 3 files to Solution folder
  4. Pack with
    pack.ps1
    (NOT
    Compress-Archive
    )
  5. Import with
    pac solution import
  6. Verify with
    pac solution list

Scenario 1b: PCF Control with Platform Libraries (Large Controls)

Use when: PCF bundle exceeds 5MB due to bundled React/Fluent UI.

If your bundle is > 1MB, you're likely bundling React and Fluent UI. Use platform libraries so Dataverse provides these at runtime.

Check Bundle Size

ls -la out/controls/*/bundle.js
# Should be 300-500KB with platform libraries, 5MB+ without

Fix ControlManifest.Input.xml

Add

<platform-library>
elements:

<resources>
  <code path="index.ts" order="1" />
  <!-- Host-provided: DO NOT bundle React/Fluent -->
  <platform-library name="React" version="16.14.0" />
  <platform-library name="Fluent" version="9.46.2" />
</resources>

Create featureconfig.json (CRITICAL)

pcf-scripts requires a feature flag to externalize ReactDOM:

{
  "pcfReactPlatformLibraries": "on"
}

Without this file, React is externalized but ReactDOM is still bundled, causing React version mismatch errors at runtime.

Enable Custom Webpack for Icon Tree-Shaking (CRITICAL for large bundles)

If your bundle is still large (>500KB) after adding platform libraries,

@fluentui/react-icons
is likely not tree-shaking. The full icon library is ~6.8MB.

Solution: Enable custom webpack with

sideEffects: false
for icons:

  1. Update featureconfig.json (add
    pcfAllowCustomWebpack
    ):
{
  "pcfReactPlatformLibraries": "on",
  "pcfAllowCustomWebpack": "on"
}
  1. Create webpack.config.js in control root:
// Custom webpack configuration for PCF
// Enables tree-shaking for @fluentui/react-icons
module.exports = {
  optimization: {
    usedExports: true,
    sideEffects: true,
    innerGraph: true,
    providedExports: true
  },
  module: {
    rules: [
      {
        // Mark @fluentui/react-icons as side-effect-free
        test: /[\\/]node_modules[\\/]@fluentui[\\/]react-icons[\\/]/,
        sideEffects: false
      }
    ]
  }
};

Result: Bundle size typically drops from 5-9MB to 200-400KB.

⚠️ NOTE:

pac pcf push
rebuilds in development mode, ignoring these optimizations. Use the pack.ps1 workflow instead to preserve production build.

Fix package.json

Move React/Fluent to

devDependencies
(type-checking only):

{
  "devDependencies": {
    "@types/react": "^16.14.0",
    "@types/react-dom": "^16.9.0",
    "react": "^16.14.0",
    "react-dom": "^16.14.0",
    "@fluentui/react-components": "^9.46.0"
  }
}

Note: Use React 16 types to match the platform runtime. See ADR-022.

Remove from

dependencies
: any
react
,
react-dom
, or
@fluentui/react-*
packages (keep in devDependencies only).

Full details: See

docs/guides/PCF-DEPLOYMENT-GUIDE.md


Scenario 1c: PCF Control in Custom Page (COMPLEX)

Use when: PCF control is embedded in a Canvas App Custom Page.

⚠️ WARNING: This is the most complex deployment scenario. When a PCF is used inside a Custom Page, THREE version locations must stay synchronized.

See detailed guide:

docs/guides/PCF-DEPLOYMENT-GUIDE.md
(Custom Page section)

Quick Summary

LocationPurposeUpdated By
Dataverse RegistryMaster version
pac pcf push
or Solution Import
Solution Controls FolderExported artifactManual copy
Canvas App EmbeddedRuntime bundleManual copy +
pac canvas pack

The Problem

When you open a Custom Page in Power Apps Studio, it may downgrade your PCF if the Dataverse Registry has an older version.

Key Rules

  1. ALWAYS update Dataverse Registry FIRST before opening Power Apps Studio
  2. ALWAYS copy bundle to BOTH locations (Controls folder AND Canvas App embedded)
  3. NEVER open Power Apps Studio until all three locations are synchronized

Scenario 1d: PCF Production Release (Full Solution Workflow)

Use when: Deploying a PCF update with proper version tracking for production.

⚠️ CRITICAL:

pac pcf push
does NOT update your named solution's version. Use this workflow for production releases.

See detailed guide:

docs/guides/PCF-DEPLOYMENT-GUIDE.md

Why This Workflow?

MethodUpdates Dataverse ControlUpdates Named Solution VersionBest For
pac pcf push
✅ Yes❌ No (creates temp solution)Dev testing
Solution Import✅ Yes✅ YesProduction releases

Quick Steps

# 1. Build
cd src/client/pcf/{ControlName}
npm run build:prod

# 2. Update version in 4 locations (manual)

# 3. Copy bundle to solution folder
cp out/controls/control/bundle.js \
   infrastructure/dataverse/solutions/{Solution}/Controls/{namespace}.{ControlName}/

# 4. Pack and import
pac solution pack --zipfile Solution_vX.Y.Z.zip --folder {Solution}
pac solution import --path Solution_vX.Y.Z.zip --publish-changes

# 5. Verify
pac solution list | grep -i "{SolutionName}"

Scenario 2: Solution Export

Use when: Backing up or extracting a solution for modification.

# List available solutions
pac solution list

# Export unmanaged solution (ALWAYS use unmanaged)
pac solution export --name "{SolutionName}" --path "./{SolutionName}.zip" --managed false

⚠️ NEVER export as managed unless the user explicitly requests it. Managed solutions have caused issues in past projects.


Scenario 3: Solution Import

Use when: Deploying a solution package to an environment.

# Import and publish in one step
pac solution import --path "./{SolutionName}.zip" --publish-changes

# Force import (overwrites conflicts)
pac solution import --path "./{SolutionName}.zip" --force-overwrite --publish-changes

Post-Import Verification

# Check solution was imported
pac solution list | grep -i "{SolutionName}"

# If not auto-published, publish manually
pac solution publish

Scenario 4: Web Resource Deployment

Use when: Deploying JavaScript, CSS, or image files.

Include web resources in a solution and use Scenario 3.

# Export solution containing web resources
pac solution export --name "SpaarkeCore" --path "./SpaarkeCore.zip" --managed false

# Extract, modify, repack, import
unzip SpaarkeCore.zip -d SpaarkeCore_extracted
# ... modify files in WebResources folder ...
pac solution pack --zipfile SpaarkeCore_modified.zip --folder SpaarkeCore_extracted
pac solution import --path SpaarkeCore_modified.zip --publish-changes

Scenario 5: Publish Customizations

Use when: Making customizations visible to users.

# Publish all customizations
pac solution publish

# Or use publish-all for everything
pac solution publish-all

Conventions

Publisher

Always use

Spaarke
publisher with prefix
sprk_
- NEVER create a new publisher.

SettingValue
Unique Name
Spaarke
Prefix
sprk
Option Value Prefix
65949

Naming examples:

  • PCF controls:
    sprk_Spaarke.Controls.{ControlName}
  • Web resources:
    sprk_FileName.js
  • Entities:
    sprk_entityname
  • Fields:
    sprk_fieldname

Working Directories

Component TypeDirectory
PCF Controls
src/client/pcf/{ControlName}
Web Resources JS
src/client/webresources/js/
Ribbon XML
infrastructure/dataverse/ribbon/
Solutions
infrastructure/dataverse/solutions/

Error Handling

ErrorCauseSolution
No active authentication profile
Not logged inRun
pac auth create --environment "URL"
NU1008: Projects that use central package version management
Directory.Packages.props conflictDisable CPM before PAC commands
Unable to remove directory "obj\Debug\Metadata"
File lock during cleanupHarmless - import the packed solution directly
Solution not found
Wrong solution nameRun
pac solution list
to find exact name
Publisher not found
Wrong publisher prefixUse
--publisher-prefix sprk
Import failed: Dependency not found
Missing dependent solutionImport dependencies first
File exceeds 5MB limit
React/Fluent bundledUse platform libraries (Scenario 1b)
PCF version regresses in Custom PageRegistry has older versionUpdate registry FIRST (Scenario 1c)
PowerAppsToolsTemp
solutions appear
Created by
pac pcf push
Delete after deployment if needed
Cannot create property '_updatedFibers'
Using React 18 APIs with React 16 runtimeUse
ReactDOM.render()
, not
createRoot()
- see ADR-022
createRoot is not a function
Importing from
react-dom/client
Import from
react-dom
instead
Solution zip not createdpack.ps1 failedCheck pack.ps1 script exists and run manually
Orphaned component blocking deployment
Namespace changed or old controls existDelete orphaned controls via Web API (see below)
CustomControls Source File styles.css does not exist
styles.css not copied to solution folderCopy ALL 3 files to Solution folder
Solution import succeeds but UI shows old behaviorControl manifest version not updatedUpdate ControlManifest.Input.xml version FIRST, rebuild, then deploy
Deployment seems stuck on old versionDataverse control cacheDelete solution with
pac solution delete
, then reimport fresh
Solution imports but shows 0 componentsEmpty
Customizations.xml
Use
pac pcf push
fallback (see below)

🚨 Solution Imports But Is Empty (0 Components)

Symptoms:

  • pac solution import
    succeeds
  • pac solution list
    shows solution with correct version
  • But solution in portal shows "All: 0" - no components
  • PCF control doesn't appear or doesn't update

Root Cause:

The

Customizations.xml
file in your Solution folder has empty component sections:

<CustomControls />
<WebResources />

When you run

pac solution pack
, it packs exactly what's in
Customizations.xml
. If the component references are missing, you get an empty solution that imports but does nothing.

Solution - Use

pac pcf push
as Fallback:

cd src/client/pcf/{ControlName}

# pac pcf push generates proper Customizations.xml automatically
pac pcf push --publisher-prefix sprk

# If you get file lock error during cleanup, ignore it - solution is already packed
# Import the generated solution
pac solution import --path "out/PowerAppsTools_sprk/bin/Debug/PowerAppsTools_sprk.zip" --publish-changes

# Verify
pac solution list | grep -i "{ControlName}"

Why This Works:

pac pcf push
generates a proper
Customizations.xml
with component references:

<CustomControls>
  <CustomControl>
    <Name>sprk_Spaarke.Controls.UniversalDocumentUpload</Name>
    <FileName>/Controls/sprk_Spaarke.Controls.UniversalDocumentUpload/ControlManifest.xml</FileName>
  </CustomControl>
</CustomControls>

Prevention:

After using

pac pcf push
successfully:

  1. Examine the generated
    Customizations.xml
    and update your Solution folder's version to match the structure
  2. IMPORTANT:
    pac pcf push
    bypasses the Solution folder entirely - it builds from source and deploys directly to Dataverse. You must still copy the fresh bundle.js and ControlManifest.xml to your Solution folder to keep it in sync for future
    pac solution pack
    deployments.
# After pac pcf push, sync Solution folder:
cp out/controls/control/bundle.js \
   out/controls/control/ControlManifest.xml \
   Solution/src/WebResources/sprk_Spaarke.Controls.{ControlName}/

This prevents the Solution folder from becoming stale and ensures future solution imports work correctly.

Orphaned Control Cleanup

When namespace changes (e.g.,

Spaarke.PCF
Spaarke.Controls
) or old deployments exist, orphaned controls can block new deployments.

Symptoms:

  • Deployment fails with duplicate component errors
  • Multiple versions of same control in solution list
  • "Component with same name already exists" errors

Solution - Delete via Web API:

# 1. Find the orphaned control's ID
# Use Dataverse Web API or Advanced Find

# 2. Delete using PAC CLI or Web API
pac org fetch --filter "customcontrolid eq 'GUID-HERE'"

# 3. Or use Power Platform Admin Center:
# - Go to Environments → Your Environment → Settings → Solutions
# - Find and delete orphaned components

Prevention:

  • Always use consistent namespace (e.g.,
    Spaarke.Controls
    )
  • Delete old solutions before changing namespace
  • Use
    pac solution delete
    to cleanly remove old solutions

Quick Reference Commands

# Authentication
pac auth list                           # Show all auth profiles
pac auth create --environment "URL"     # Create new profile
pac auth select --index N               # Switch profile

# Solutions
pac solution list                       # List all solutions
pac solution export --name X --path Y   # Export solution (add --managed false)
pac solution import --path Y            # Import solution
pac solution publish                    # Publish customizations

# PCF Controls - Use pack.ps1 workflow (see Deployment Workflow above)
npm run build:prod                      # Build control
powershell -File Solution/pack.ps1      # Pack solution (NOT Compress-Archive)
pac solution import --path bin/X.zip    # Import solution

# Troubleshooting
pac org who                             # Show current org info
pac solution check --path Y             # Validate solution before import

Related Skills

  • ribbon-edit
    - Ribbon customizations use solution export/import workflow
  • spaarke-conventions
    - Naming conventions for all Dataverse components
  • adr-aware
    - ADR-006 governs PCF control patterns, ADR-022 governs React version
  • ci-cd
    - CI/CD pipeline status and automated deployment workflows

CI/CD Integration

Automated Plugin Deployment via GitHub Actions

Plugin deployments can be automated via the

deploy-staging.yml
workflow:

WorkflowTriggerWhat It Deploys
deploy-staging.yml
Auto (after CI passes on master) or ManualDataverse plugins via PAC CLI

Workflow Plugin Deployment

The

deploy-plugins
job in
deploy-staging.yml
:

  1. Downloads build artifacts from CI
  2. Authenticates with Power Platform using service principal
  3. Deploys plugin assembly via PAC CLI
pac auth create --url $POWER_PLATFORM_URL --applicationId $CLIENT_ID --clientSecret $SECRET
pac plugin push --path ./artifacts/publish/plugins/Spaarke.Plugins.dll

When to Use Manual vs Automated

ScenarioUse
Plugin code changes merged to masterAutomated (
deploy-staging.yml
)
PCF control iterative developmentManual (this skill - Quick Dev Deploy)
Production solution releaseManual (this skill - Scenario 1d)
Custom Page updatesManual (this skill - Scenario 1c)
Emergency hotfixManual (this skill)

Required Secrets for Automated Deployment

SecretPurpose
POWER_PLATFORM_URL
Dataverse environment URL
POWER_PLATFORM_CLIENT_ID
Service principal app ID
POWER_PLATFORM_CLIENT_SECRET
Service principal secret

Monitor Automated Deployments

# View staging deployment status
gh run list --workflow=deploy-staging.yml

# View specific deployment run
gh run view {run-id}

# Check deploy-plugins job
gh run view {run-id} --log --job=deploy-plugins

Manual Trigger of Plugin Deployment

# Trigger staging deployment with plugins
gh workflow run deploy-staging.yml -f deploy_plugins=true

Related ADRs

ADRRelevance
ADR-006PCF over legacy webresources
ADR-022React 16 compatibility (CRITICAL)
ADR-021Fluent UI v9 design system

Resources

ResourcePurpose
docs/guides/PCF-DEPLOYMENT-GUIDE.md
Primary guide - Consolidated PCF deployment workflow

Tips for AI

🚨 CRITICAL: Control Manifest Version is the Cache Key

When deploying PCF updates, the #1 cause of "deployment succeeded but nothing changed" is forgetting to update the control manifest version. Follow this exact order:

  1. FIRST: Update
    control/ControlManifest.Input.xml
    version attribute
  2. THEN: Rebuild with
    npm run build:prod
    (or
    pcf-scripts build --buildMode production
    )
  3. THEN: Copy ALL 3 files to Solution folder
  4. THEN: Update
    solution.xml
    and
    pack.ps1
    versions
  5. THEN: Pack and import

If the user reports the control isn't updating after deployment:

  1. Check if
    ControlManifest.Input.xml
    version was incremented
  2. If not, increment it, rebuild, and redeploy
  3. If still stuck, use the nuclear option:
    pac solution delete
    then reimport

Never assume that updating

solution.xml
alone will bust the cache. The control manifest version is what Dataverse checks.


🚨 CRITICAL: Solution Imports But Shows 0 Components

If

pac solution pack
+
pac solution import
succeeds but the solution is empty in the portal:

  1. Root cause:
    Customizations.xml
    has empty
    <CustomControls />
    section
  2. Fix: Use
    pac pcf push --publisher-prefix sprk
    as fallback
  3. Why it works:
    pac pcf push
    auto-generates proper component references
  4. File lock error during cleanup is harmless - solution is already packed, import it directly
  5. CRITICAL: After
    pac pcf push
    , sync the Solution folder -
    pac pcf push
    bypasses it entirely, leaving it stale:
    cp out/controls/control/bundle.js out/controls/control/ControlManifest.xml \
       Solution/src/WebResources/sprk_Spaarke.Controls.{ControlName}/
    
  6. Prevention: Copy the generated
    Customizations.xml
    structure to your Solution folder for future
    pac solution pack
    deployments