plugins/aem/cloud-service/skills/migration/SKILL.md
Migrates legacy AEM (6.x, AMS, on-prem) to AEM as a Cloud Service using BPA CSV or cache, CAM/MCP target discovery, and a one-pattern-per-session workflow. Use for BPA/CAM findings, Cloud Service blockers, or fixes for scheduler, ResourceChangeListener, replication, EventListener, OSGi EventHandler, DAM AssetManager, HTL data-sly-test lint, Classic UI dialog migration (lui — ExtJS/Coral 2 → Coral 3), and Custom Design Widgets (cdw). OSGi configs → Cloud Manager — scan ui.config, .cfg.json, secrets, $[secret:]/$[env:] — agent follows references/osgi-cfg-json-cloud-manager.md when prompted. After BPA/CAM discovery, migration hands off each (pattern, file) pair to the code-assessment skill for scheduler/resource-change-listener/replication/event-migration/asset-manager guides and shared SCR→DS/ResourceResolver/HTL-lint references. Template modernization runs a per-template context → execute → validate pipeline. Legacy UI migration (dialog and CDW) follows references/legacy-ui/ modules.
npx skillsauth add adobe/skills migrationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Source → target: Legacy AEM 6.x / AMS / on-prem → AEM as a Cloud Service. Scoped under skills/aem/cloud-service/skills/migration/ so this is not confused with Edge Delivery or 6.5 LTS.
This skill drives the migration workflow: BPA data, CAM/MCP, one pattern per session, and target discovery. Transformation rules and steps live in the code-assessment skill — once a finding's pattern is identified, hand off to {code-assessment}/<pattern>/SKILL.md (or the relevant shared reference under {code-assessment}/references/).
Setup: Use the aem-cloud-service install (see repository root README) so both migration and code-assessment paths are available. If you already have the monorepo open with resolvable {code-assessment} paths, no separate install step is required.
One pattern per chat/session — if you ask to "fix everything," the skill will ask you to pick first (e.g. scheduler vs replication vs htlLint).
| You have… | Say something like… | What happens |
|-----------|---------------------|--------------|
| A BPA CSV | "Fix scheduler findings using ./path/to/bpa.csv" | Fastest path: CSV → cached collection → files |
| CAM + MCP only | "Get scheduler findings from CAM; I'll pick the project when you list them." | Agent lists projects → you confirm → MCP fetch (cam-mcp.md) |
| Just a few files | "Migrate scheduler in core/.../MyJob.java" | Manual flow: no BPA required |
| OSGi → Cloud Manager | "Scan my config files and create Cloud Manager environment secrets or variables." | Agent auto-reads references/osgi-cfg-json-cloud-manager.md (full Adobe-aligned rules inlined there); no BPA pattern id |
| HTL lint warnings | "Fix htlLint issues in ui.apps" | Proactive discovery via rg → fix per the HTL lint reference |
| Template modernization | "Migrate my static templates to editable templates and generate Modernize Tools rules." / "Create editable templates from my static templates." / "Generate AEM Modernize Tools structure/component/policy rules." | Agent auto-reads references/template-modernization/template-modernization-context.md (shared discovery + structured context), produces a per-template plan table, then executes the plan using editable-template-creation.md and aem-modernization.md, and validates via template-modernization-validation.md. No BPA pattern id. |
| Dialog migration | "Convert my Classic UI / ExtJS dialogs to Touch UI." / "Upgrade Coral 2 dialogs to Coral 3." / "Fix LUI dialog findings." | Agent reads references/legacy-ui/dialog/context.md — filters BPA LUI to dialog sub-types, converts via extjs-to-coral3.md or coral2-to-coral3.md, validates via validation.md. BPA pattern id: lui. |
| Custom widget migration | "Fix my CDW findings." / "Migrate custom ExtJS widgets to Coral 3." | Agent reads references/legacy-ui/cdw/context.md — inventories xtypes, maps or scaffolds Granite UI components via conversion.md, validates via validation.md. BPA pattern id: cdw. Run CDW before dialog migration when both are needed. |
Starter prompts (copy-paste):
./reports/bpa.csv, then apply the code-assessment pattern guide before editing.".../Listener.java — read the code-assessment pattern guide first."ui.apps — scan for data-sly-test redundant constant warnings and fix them."./reports/bpa.csv."From the repository root (parent of the skills/ directory):
| Symbol | Path |
|--------|------|
| {code-assessment} | skills/aem/cloud-service/skills/code-assessment/ |
Examples: {code-assessment}/SKILL.md, {code-assessment}/scheduler/SKILL.md, {code-assessment}/references/scr-to-osgi-ds.md.
Applies to finding and editing the user's AEM project (Java, bundles, config, HTL), not to reading installed skill files under {code-assessment}.
grep, and file reads/writes for migration targets.~, other clones, or arbitrary absolute paths to "discover" sources unless the user explicitly names those paths or asks you to include them.filePath or class-to-file mapping does not resolve under a workspace root, stop and tell the user which paths are missing — do not hunt elsewhere on the filesystem. Ask them to open the correct project in the IDE or adjust paths.Branch A — OSGi configs → Cloud Manager (no Java BPA pattern this session): If the user asks to scan config files, create / set up Cloud Manager environment secrets or variables, move passwords or secrets out of OSGi / .cfg.json / ui.config, or mentions $[secret:] / $[env:] for AEM CS, then read references/osgi-cfg-json-cloud-manager.md immediately and follow the product rules and workflow defined in that file (Adobe AEM as a Cloud Service OSGi + Cloud Manager behavior is reproduced there—no external doc URL required). Sleek prompts are enough — no need to name the reference file. Skip branch B for that work.
Branch B — Java / HTL / BPA pattern migration:
{code-assessment}/SKILL.md — critical rules, Java baseline links, Pattern Guides table, Manual Pattern Hints.scheduler → {code-assessment}/scheduler/SKILL.md (pattern guide)resourceChangeListener → {code-assessment}/resource-change-listener/SKILL.md (pattern guide)replication → {code-assessment}/replication/SKILL.md (pattern guide)eventListener / eventHandler → {code-assessment}/event-migration/SKILL.md (pattern guide — both JCR and OSGi Event Admin paths)assetApi → {code-assessment}/asset-manager/SKILL.md (pattern guide)htlLint → {code-assessment}/references/data-sly-test-redundant-constant.md (reference — HTL lint is a single shared reference, not a dedicated pattern guide)ResourceResolver, or console logging, read {code-assessment}/references/scr-to-osgi-ds.md and {code-assessment}/references/resource-resolver-logging.md (or the hub {code-assessment}/references/aem-cloud-service-pattern-prerequisites.md).Do not transform Java or HTL until the pattern guide (or reference) is read (branch B). Branch A does not require {code-assessment} pattern guidance.
Branch C — Template Modernization (no BPA): static → editable templates and/or AEM Modernize Tools rules (structure/component/policy). Three phases: context → per-template execute → validate. Start at references/template-modernization/template-modernization-context.md; generators are editable-template-creation.md and aem-modernization.md; post-gen checks in template-modernization-validation.md. Skip branch B.
Branch D — Legacy UI Migration (legacy-ui/ sub-folders): If the user asks to convert Classic UI / ExtJS dialogs, upgrade Coral 2 dialogs, migrate custom ExtJS widgets, fix LUI or CDW BPA findings, or mentions cq:Dialog / xtype / cq:Widget:
For dialog findings (lui pattern, legacy.dialog.classic or legacy.dialog.coral2 only):
getBpaFindings('lui', …), filter to dialog sub-types, skip all others with a note.convert-extjs → references/legacy-ui/dialog/extjs-to-coral3.md.upgrade-coral2 → references/legacy-ui/dialog/coral2-to-coral3.md.For custom widget findings (cdw pattern):
getBpaFindings('cdw', …), inventory xtypes.legacy.dialog.classic findings, run dialog migration afterwards — all xtypes are now resolved.Run order when both are needed: CDW first, then dialog. CDW resolves custom xtypes so dialog conversion can proceed without stops. Skip Branch B. Skip Branch C.
data-sly-test: redundant constant value comparison) across component templates.cfg.json, apply $[secret:…] / $[env:…] per rules in references/osgi-cfg-json-cloud-manager.md; gitignored handoff; no secret values in chat.legacy-ui/dialog/ for Classic UI/Coral 2 dialog conversion (BPA lui dialog sub-types); legacy-ui/cdw/ for custom ExtJS widget remediation (BPA cdw). Run CDW before dialog when both are needed.Sleek user prompts are enough (see Quick start). Agent: Branch A → read the reference → One-prompt workflow; obey the inlined Adobe AEM CS rules in that file (value types, placeholders, CM API/CLI, custom-properties-only, repoinit, runmode context, local SDK secrets). Ambiguous or Adobe-owned PIDs → needs_user_review, not guesses.
ui.apps or equivalent content package with .html HTL templatesScripts run via getBpaFindings (see Calling the helper); do not reimplement collection logic by hand unless the helper is unavailable.
The helper has two independent paths, chosen by what the caller configures:
mcpFetcher + projectId passed) → first call fetches all findings
from MCP and caches them to <collectionsDir>/mcp/<projectId>/<pattern>.json.
Every call (first and subsequent) reads from the MCP cache and returns one batch.<collectionsDir>/unified-collection.json.
Every call reads from the CSV cache and returns one batch.The two caches are disjoint — MCP sessions and CSV sessions never shadow each other. If
neither is configured, the helper reports no-source and the agent asks for one.
Batching is mandatory on every path. getBpaFindings returns findings in batches of 5
by default with a paging envelope:
result.targets // this batch (length <= limit)
result.paging // { total, returned, offset, limit, nextOffset, hasMore }
Process one batch at a time; stop after each batch and report progress to the user; resume on
the user's go-ahead by re-calling the helper with offset: paging.nextOffset. See
Batched processing (batch size 5) below.
Note: htlLint does not appear in BPA CSV — it uses proactive rg discovery instead. See htlLint flow below.
Use fetch-cam-bpa-findings-by-pattern for code-transformer pattern flows (scheduler,
assetApi, eventListener, resourceChangeListener, eventHandler, lui, cdw) and
fetch-cam-bpa-findings-by-importance when the user instead asks "what are the
critical/major/advisory/info findings?" (returns the latest BPA report's authoritative
_COUNT_<code> rows at one importance level, sorted by descending count). Either tool
requires explicit user confirmation of the project before being called — ask the user
for their CAM project name or ID; the tools resolve it internally (prefer projectId
when known). Do not pass an unconfirmed project name string. Full tool schemas, REST notes, retries, and error handling:
references/cam-mcp.md.
core/.../Foo.java" → manual flowScripts live under ./scripts/ (next to this SKILL.md).
const { getBpaFindings } = require('./scripts/bpa-findings-helper.js');
// First batch (defaults: limit=5, offset=0)
const result = await getBpaFindings(pattern, {
bpaFilePath: './cleaned_file6.csv',
collectionsDir: './unified-collections',
projectId: '...',
mcpFetcher: mcpFunction
// limit: 5, // implicit default
// offset: 0, // implicit default
});
// Next batch — only after the user says to continue
if (result.paging?.hasMore) {
const next = await getBpaFindings(pattern, {
bpaFilePath: './cleaned_file6.csv',
collectionsDir: './unified-collections',
projectId: '...',
mcpFetcher: mcpFunction,
offset: result.paging.nextOffset
});
}
result:
success, source ('unified-collection' | 'bpa-file' | 'mcp-server' | …)message (includes a human-readable batch status)targets — the current batch (length <= limit)paging: { total, returned, offset, limit, nextOffset, hasMore } — always present on
successful callsTo disable batching for a one-off programmatic caller, pass limit: null. The
skill workflow itself never does this.
Collections live under ./unified-collections/. If a collection exists and the user supplies a new CSV, ask whether to reuse or re-process.
Filter rows where pattern matches the session pattern. Typical columns: pattern, filePath, message.
Critical: On MCP failure, stop the workflow immediately and give the user the exact tool error message (verbatim), including "not found" / 404-style project errors. Do not continue with migration steps, infer a different CAM project from the workspace, or switch to manual/local migration on your own.
Exception: enablement restriction errors (prefix documented in references/cam-mcp.md) must be shown verbatim with no paraphrase and no automatic fallback until the user addresses them.
After stopping, you may summarize what failed in plain language and, if helpful, re-show projects from list-projects. Only continue when the user explicitly directs the next step (e.g. correct project id/name from the list, BPA CSV path, or specific Java files for manual flow).
For retries, error categories, and when user-directed CSV/manual paths are allowed, follow references/cam-mcp.md; still no silent fallback. Never hide tool errors from the user.
Optional prompt after stop (user must reply): "Reply with the CAM project to use (id or name from the list), a path to your BPA CSV, or the Java files for a manual migration."
Do not duplicate the pattern table here. Use {code-assessment}/SKILL.md → Pattern Guides — five patterns each have a pattern guide ({code-assessment}/<pattern>/SKILL.md); shared topics (SCR→DS, ResourceResolver/SLF4J, HTL lint, prerequisites hub) stay as references ({code-assessment}/references/<file>.md). See Branch B step 2 above for the per-pattern routing table.
If the user asks to fix everything or BPA mixes patterns, ask which pattern first. Prefer one commit per pattern session.
If the request is OSGi configs → Cloud Manager (see Required delegation, branch A), do not map to a BPA pattern — follow references/osgi-cfg-json-cloud-manager.md instead.
If the request is template modernization — including "create editable templates", "generate /conf templates", "static to editable template", "structure rewrite rules", "component rewrite rules", "policy import rules", "parsys to container", or "AEM Modernize Tools" — follow Branch C → start with references/template-modernization/template-modernization-context.md (discovery + plan table), then execute per-template via the generators, then validate. No pattern id, no BPA.
If the request involves legacy UI (Classic UI dialogs, Coral 2 dialogs, custom ExtJS widgets, LUI or CDW BPA findings) — follow Branch D. Route lui findings to legacy-ui/dialog/, cdw findings to legacy-ui/cdw/. No Java pattern modules needed.
Otherwise map the request to a pattern id: scheduler, resourceChangeListener, replication, eventListener, eventHandler, assetApi, htlLint, lui, cdw. If unclear, use Manual Pattern Hints in {code-assessment}/SKILL.md or ask the user to pick one of those.
If the id is missing from the code-assessment catalog ({code-assessment}/references/patterns.md), say the pattern is not supported yet.
For BPA patterns (scheduler, resourceChangeListener, replication, eventListener, eventHandler, assetApi, lui, cdw): Run getBpaFindings (with bpaFilePath when provided). Internally: cache → CSV → MCP → manual only when each step is applicable and succeeds; if MCP fails, obey MCP errors and fallback (stop; no silent chain). For MCP details, references/cam-mcp.md.
For lui findings, the identifier in each target is the JCR component path (e.g. /apps/myapp/components/content/mycomp) — not a Java class name. Resolve it to the filesystem path using the JCR → filesystem mapping before opening files. Note: luiCoral2 is not a standalone BPA pattern id — Coral 2 dialogs appear as the legacy.dialog.coral2 sub-type within lui results. Do not call getBpaFindings('luiCoral2', …) independently; call getBpaFindings('lui', …) and filter by sub-type inside Branch D.
getBpaFindings returns a batch of 5 findings (default limit=5) along with a paging
envelope. The agent processes that batch only; it does not request the next batch until
the user says to continue. See Batched processing (batch size 5) below.
For htlLint: Skip BPA/CSV/MCP — targets come from proactive rg discovery. See htlLint flow below.
STOP. Read {code-assessment}/SKILL.md and the pattern guide (or reference) for the active pattern — see Branch B step 2 above for the pattern → file routing table.
For each finding in the returned batch only (up to 5):
Do not request the next batch mid-processing. Never hold more than one batch of findings in working memory at a time.
After finishing the batch, summarise for this batch only:
paging.returned findings processed (of paging.total), with class names.paging.hasMore === true, tell the user:
"Processed batch of N (offset {offset}–{offset + returned − 1} of {total}). Reply
continue to process the next batch, or name specific classes to focus on."paging.hasMore === false, say the pattern is done and move to the overall session report.Stop and wait for the user. Do not automatically start the next batch. Only call
getBpaFindings (or fetch-cam-bpa-findings-by-pattern) again when the user explicitly
requests it, and pass offset: paging.nextOffset unchanged.
User-named files → classify (code-assessment Manual Pattern Hints or ask) → confirm the pattern guide or reference exists → read {code-assessment}/SKILL.md + the pattern guide (or reference) — see Branch B step 2 routing — → transform → report.
Does not use BPA CSV, CAM/MCP, or code-assessment pattern guides for collection. Follow Branch A in Required delegation and the One-prompt workflow in references/osgi-cfg-json-cloud-manager.md.
No BPA / MCP. Three phases — context → per-template execute → validate — fully defined in references/template-modernization/template-modernization-context.md. Use the confirmed context and per-template plan table first, execute generators via references/template-modernization/editable-template-creation.md and references/template-modernization/aem-modernization.md, then run references/template-modernization/template-modernization-validation.md. Do not commit on validation failure.
htlLint does not use BPA CSV or CAM/MCP. Instead:
{code-assessment}/references/data-sly-test-redundant-constant.md — it contains the Workflow, Proactive Discovery rg patterns, and all 4 fix patterns. (HTL lint lives as a shared reference, not a dedicated pattern guide.)rg commands from the reference's Proactive Discovery table (scope: ui.apps/**/jcr_root/**/*.html or the user's content package paths).mvn clean install or HTL validate to confirm no warnings remain.Findings are served to the agent in batches of 5 by default, regardless of source (MCP or CSV). Batching happens client-side — the heavy fetch (MCP call or CSV parse) happens once and is materialized to a local JSON cache; every subsequent batch is a cheap slice of that cache.
limit is 5. Pass limit: 5 (or accept the helper default). The skill never
requests a larger batch unless the user has explicitly asked for one.result.paging.nextOffset from the previous call.
Do not compute offsets from offset + limit — read nextOffset from the previous
response; it is authoritative.offset: previous.paging.nextOffset". If the session ends, a
later session calls with the same pattern and offset and gets the same batch.paging.hasMore === false (or paging.nextOffset === null).<collectionsDir>/unified-collection.json<collectionsDir>/mcp/<projectId>/<pattern>.json[User] "Fix scheduler findings using ./reports/bpa.csv"
[Agent] getBpaFindings('scheduler', { bpaFilePath, limit: 5, offset: 0 })
// first call parses CSV → writes <dir>/unified-collection.json → slices
→ paging: { total: 137, returned: 5, offset: 0, nextOffset: 5, hasMore: true }
Processes 5 findings.
Reports: "Processed 5 of 137 (offset 0–4). Reply `continue` for the next batch."
[User] "continue"
[Agent] getBpaFindings('scheduler', { bpaFilePath, limit: 5, offset: 5 })
// reads cached JSON — no CSV re-parse
→ paging: { ..., offset: 5, nextOffset: 10, hasMore: true }
Processes next 5.
...
[User] "Fix scheduler findings from CAM project <id>"
[Agent] getBpaFindings('scheduler', { mcpFetcher, projectId, limit: 5, offset: 0 })
// first call: one MCP fetch → writes <dir>/mcp/<projectId>/scheduler.json → slices
→ paging: { total: 137, returned: 5, offset: 0, nextOffset: 5, hasMore: true }
Processes 5 findings.
Reports and stops.
[User] "continue"
[Agent] getBpaFindings('scheduler', { mcpFetcher, projectId, limit: 5, offset: 5 })
// reads cached MCP JSON — NO additional MCP call
→ paging: { ..., offset: 5, nextOffset: 10, hasMore: true }
...
getBpaFindings with limit: null inside the skill flow. That option exists
only for programmatic callers that deliberately want the full list.paging.nextOffset from the previous
response.targets across batches in memory.Source priority (when choosing how to obtain targets): unified collection → BPA CSV → MCP → manual paths. Not an automatic cascade after MCP errors — if MCP fails, stop and wait for user direction (see MCP errors and fallback). For htlLint, use proactive rg discovery (no BPA/MCP). For OSGi → Cloud Manager, use references/osgi-cfg-json-cloud-manager.md only (no BPA/MCP). For Template Modernization (Branch C), use the three-file pipeline: template-modernization-context.md → (editable-template-creation.md + aem-modernization.md) → template-modernization-validation.md — no BPA/MCP. For Legacy UI (Branch D): lui dialog sub-types → references/legacy-ui/dialog/context.md; cdw → references/legacy-ui/cdw/context.md.
Batch size: 5 (default) on every BPA source. See Batched processing above.
User-facing snippets: "Using existing BPA collection (N findings)…" / "Processing your BPA report…" / "Fetched findings from CAM." / "Scanning HTL templates for data-sly-test lint issues…" / optional prompt after MCP stop above.
From this skill's directory:
# First batch (default offset=0, limit=5)
node scripts/bpa-findings-helper.js scheduler ./unified-collections
node scripts/bpa-findings-helper.js scheduler ./unified-collections ./cleaned_file6.csv
# Next batch: offset=5, limit=5
node scripts/bpa-findings-helper.js scheduler ./unified-collections ./cleaned_file6.csv 5 5
# Full unbounded listing (development / debugging only — skill never does this)
node scripts/bpa-findings-helper.js scheduler ./unified-collections ./cleaned_file6.csv 0 all
# Same batching on the low-level reader
node scripts/unified-collection-reader.js all ./unified-collections 0 5
tools
Use the run-workflow MCP to discover, compose, execute, publish, and save Adobe Firefly workflows. TRIGGER when: user asks what actions are available, what the MCP can do, how to process images/video/3D via workflow, wants to build/run/save/publish a workflow, OR pastes any workflow/batch/execution ID. BARE ID (UUID/workflowId/batchId) = INSPECT ONLY — call inspect_run, NEVER run_workflow_submit. ALWAYS call list_actions first for capability/discovery questions. DO NOT TRIGGER for direct Firefly API calls without MCP (use firefly-api-specs).
tools
Run predefined featured workflows via run-workflow MCP. TRIGGER when user names a featured workflow (retargeting, banners at scale, localization, packaging, banner advertising, etc.) or asks to run a known marketing/production workflow. Requires run-workflow MCP. ALWAYS call get_featured_workflow before compose_workflow. DO NOT TRIGGER for custom one-off workflows with no named template — use run-workflow skill.
tools
Migrate an Adobe Commerce App Builder project from the Integration Starter Kit or Checkout Starter Kit to the new App Management approach. Run from the root of the App Builder project to be migrated. Pass --auto to skip confirmation prompts (suitable for CI or batch use) — auto mode prints a summary of all Q&A questions answered with their defaults. Pass --doc-scan-only to scan README.md and env.dist for outdated content without modifying any files. Use when the user wants to migrate an App Builder project from the Integration Starter Kit or Checkout Starter Kit to the App Management approach, or mentions upgrading their Adobe Commerce extension architecture.
development
Add or modify webhook interceptors in an Adobe Commerce app. Use when the user wants to intercept Commerce operations to validate input, append data, or modify behavior — before or after execution. Requires a base app initialized with commerce-app-init.