Prepare Cloudflare Production Deployment
LubomirGeorgiev/cloudflare-workers-nextjs-saas-template
Source-of-truth runbook for preparing this Vinext Cloudflare Workers SaaS template for production deployment.
Migrates legacy AEM (6.x, AMS, on-prem) to AEM as a Cloud Service via BPA CSV/cache and CAM/MCP discovery, one pattern per session.
$ npx skills add adobe/skills --skill migration -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install adobe/skills migration --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/adobe/skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/plugins/aem/cloud-service/skills/migration .claude/skills/migration && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "migration" agent skill from https://github.com/adobe/skills/tree/main/plugins/aem/cloud-service/skills/migration into .claude/skills/migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "migration", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/adobe/skills/tree/main/plugins/aem/cloud-service/skills/migrationType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add adobe/skills --skill migration -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install adobe/skills migration --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/adobe/skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/plugins/aem/cloud-service/skills/migration .agents/skills/migration && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "migration" agent skill from https://github.com/adobe/skills/tree/main/plugins/aem/cloud-service/skills/migration into .agents/skills/migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "migration", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add adobe/skills --skill migration -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install adobe/skills migration --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/adobe/skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/plugins/aem/cloud-service/skills/migration .cursor/skills/migration && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "migration" agent skill from https://github.com/adobe/skills/tree/main/plugins/aem/cloud-service/skills/migration into .cursor/skills/migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "migration", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/adobe/skills.git --path plugins/aem/cloud-service/skills/migration--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add adobe/skills --skill migration -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install adobe/skills migration --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/adobe/skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/plugins/aem/cloud-service/skills/migration .gemini/skills/migration && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "migration" agent skill from https://github.com/adobe/skills/tree/main/plugins/aem/cloud-service/skills/migration into .gemini/skills/migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "migration", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install adobe/skills migrationInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add adobe/skills --skill migration -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/adobe/skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/plugins/aem/cloud-service/skills/migration .github/skills/migration && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "migration" agent skill from https://github.com/adobe/skills/tree/main/plugins/aem/cloud-service/skills/migration into .github/skills/migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "migration", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add adobe/skills --skill migration -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install adobe/skills migration --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/adobe/skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/plugins/aem/cloud-service/skills/migration .opencode/skills/migration && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "migration" agent skill from https://github.com/adobe/skills/tree/main/plugins/aem/cloud-service/skills/migration into .opencode/skills/migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "migration", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
migrationMigrates legacy AEM (6.x, AMS, on-prem) to AEM as a Cloud Service via BPA CSV/cache and CAM/MCP discovery, one pattern per session.
Migration is an agent skill from adobe/skills. Migrates legacy AEM (6.x, AMS, on-prem) to AEM as a Cloud Service via BPA CSV/cache and CAM/MCP discovery, one pattern per session. Use to review/scan a project for AEMaaCS migration — generates a read-only migration-runbook.md — or to fix specific Cloud Service blockers: scheduler, ResourceChangeListener, replication, EventListener, OSGi EventHandler, DAM AssetManager, HTL data-sly-test lint, Classic UI / ExtJS / Coral 2 → Coral 3 dialog migration (lui), Custom Design Widgets (cdw), Vault package install-time…
Its SKILL.md is about 11k tokens, which your agent loads only when the skill is triggered. The skill folder holds 61 other files, including scripts and reference files (for example `README.md`, `references/cam-mcp.md` and `references/dispatcher/config-generation.md`).
It sits in Development, covering Runbooks and postmortems, Legacy modernization and CSV and tabular files. It works with Adobe Experience Manager and Model Context Protocol. The repository describes itself as: Adobe Skills for Agents. The licence is Apache-2.0.
7 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit cbc9952. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Ships 1 file in scripts/, which the agent can run.
Shell commands in SKILL.md call:
nodemvnFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Migration loads about 11k tokens when it runs, and up to ~67k if it reads all its reference files. Until then it costs about 210 tokens; SKILL.md has 4,819 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.
The full file from adobe/skills at commit cbc9952, republished under its Apache-2.0 licence (© adobe). 4,819 words, ~11,392 tokens.
.claude/skills/migration/SKILL.md (or your agent's skills folder). This skill also uses 56 other files; get the full folder from GitHub.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 whole project to assess | "Review my code for AEMaaCS migration" | Generates read-only migration-runbook.md — all migration patterns (Java cascade + htlLint + osgiConfig), affected files, sample prompts. No edits. |
| 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 |
| Vault package dependencies | "Fix vault-package-dependencies findings" / "Package install fails on AEMaaCS." | Agent reads references/vault-package-dependencies.md — heuristic pom.xml scan (no BPA subtype, no analyzer) for day/cq60/product:* install-time deps in content-package-maven-plugin; removes the whole <dependencies> block. Not a code-assessment pattern — this dependency shape never occurs in native AEMaaCS code. |
| 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. |
| Guava cache warnings | "Fix guavaCache findings using BPA CSV." / "Swap Guava cache for Caffeine." | Agent reads references/guava-cache.md — BPA is the source of truth (subtype custom.guava.cache); one finding per bundle, not per Guava-internal class row. BPA pattern id: guavaCache. Not a code-assessment pattern — Guava cache usage only occurs in pre-migration code, never native AEMaaCS code. |
| Dispatcher conversion | "Convert my AMS / on-prem Dispatcher config to AEM as a Cloud Service." | Agent reads references/dispatcher/context.md — detects the config mode, generates the tool config, runs Adobe's dispatcher-converter, verifies output (filter/ACL hard-gate), and validates. Branch E. Runbook pattern id: dispatcherConversion (heuristic). |
Starter prompts (copy-paste) — the Quick start table above covers each pattern individually; these add the whole-project entry point, source-specific invocations (CSV / CAM / manual), and multi-step combinations:
./reports/bpa.csv.".../Listener.java."./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)vault-package-dependencies → references/vault-package-dependencies.md (reference — heuristic pom.xml scan; lives under migration only, not code-assessment, since this dependency shape never occurs in native AEMaaCS code)guavaCache → references/guava-cache.md (reference — Guava cache → Caffeine swap; lives under migration only, not code-assessment, since Guava cache usage does not occur in native AEMaaCS code, only in code carried over from legacy AEM)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.
Branch E — Dispatcher Conversion (AMS / on-premise Apache httpd + Dispatcher → AEMaaCS; no Java BPA pattern this session):
If the user asks to convert / migrate a Dispatcher configuration to AEM as a Cloud Service, follow the 6-phase flow. It wraps Adobe's maintained @adobe/aem-cs-source-migration-dispatcher-converter as the conversion engine and adds detection, config generation, output verification, judgment, and validation on top. Start by reading references/dispatcher/context.md. Skip Branch B.
scripts/dispatcher-inventory.js (buildInventory) to detect the mode (standard / flexible / ams / already-cloud / not-dispatcher / v1 / unknown) and count filter / rewrite / cache rules. (ams is a monolithic AMS on-premise config — converted via the same on-prem executor as flexible, just labelled honestly.) Modes and signals are defined in references/dispatcher/context.md. If the mode is already-cloud, not-dispatcher, or unknown, STOP with that finding — the first two have nothing to convert, and unknown is an ambiguous/incomplete layout to confirm with the user before running the content-blind tool (resolveExecutor falls unknown through to the on-prem executor, so the agent is the gate here).config.yaml — build the converter config per references/dispatcher/config-generation.md (per-mode mapping; variablesToReplace is a flat mapping, portsToMap is a list, appendToVhosts is a file path).ensureToolInstalled (auto-installs the Adobe tool into the gitignored scripts/dispatcher-tool/node_modules/ on first use) then runConverter (scripts/dispatcher-run.js); the executor is selected by mode (standard → main.js, flexible / on-prem → singleFileMain.js).scripts/dispatcher-verify.js and apply references/dispatcher/output-verification.md. HARD STOP on filter-acl-loss (an empty filters.any when the baseline had filter rules): the conversion is not usable until it is resolved.scripts/dispatcher-crossboundary.js to build the Cloud Manager variable handoff artifact, then apply references/dispatcher/cross-boundary.md to route each concern — CM vars → Branch A; immutable freshness → the dispatcher skill's sdk(diff-baseline); security headers / edge → security-hardening; validation → config-authoring.src per references/dispatcher/validation.md (delegates to the dispatcher skill's SDK validator + guardrails); iterate until clean. Do not present the result as done on validation failure. After validation, render the consolidated report with scripts/dispatcher-report.js (renderReport → writeReport) as conversion-report.md, which includes the coverage counts, the CM handoff, and the delegated next-checks checklist.data-sly-test: redundant constant value comparison)day/cq60/product:*) blocking package installation on AEMaaCScom.google.common.cache.*) for Caffeine (guavaCache)dispatcher-converter, with mode detection, config generation, output verification (filter/ACL hard-gate), cross-boundary handoff, and SDK validation. References: references/dispatcher/.Branch routing and the read-first delegation for each entry above are defined once in Required delegation — this list is only the "when."
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 a batch of 5 (result.targets) plus a result.paging envelope { total, returned, offset, limit, nextOffset, hasMore }. Process one batch, report, stop, and resume only on the user's go-ahead — full rules in 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, guavaCache, 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.
Scripts 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); vault-package-dependencies is a migration-only reference (references/vault-package-dependencies.md), not a {code-assessment} pattern guide. See Branch B step 2 above for the per-pattern routing table.
When the user opens with a broad review/scan request — "review my code for AEMaaCS migration", "scan my project for AEM migration", or similar — and does not name a single pattern, generate a read-only migration runbook before any apply work.
The runbook covers every pattern the migration skill can address. Each pattern declares a detection strategy — CSV-eligible patterns run the priority cascade; the others keep their existing discovery behaviour:
| Pattern(s) | Strategy | How it's detected |
|---|---|---|
scheduler, resourceChangeListener, event-migration, assetApi | cascade | BPA/CAM → CSV → analyzer → LLM scan (priority list) |
replication | cascade | analyzer → LLM scan (no BPA/CSV subtype mapping) |
htlLint | html-scan | heuristic regex scan of .html (pure Node — no rg binary needed) |
osgiConfig | config-scan | heuristic scan of OSGi config files for secret-looking keys / $[secret:]/$[env:] placeholders — key names + locations only, never secret values |
vault-package-dependencies | pom-scan | Prefers Maven's effective POM (mvn help:effective-pom, one call per reactor root — <pluginManagement> inheritance and per-execution vs plugin-level <configuration> are Maven's problem, not ours). Falls back to a raw pom.xml text-scan when Maven can't resolve a module (dead parent repos, offline — common for legacy AEM 6.x / AMS codebases). No BPA subtype and no analyzer — a pom.xml install-time dependency declaration is invisible to a deployed-artifact BPA scan and there is no compiled detector for it. Every effective-POM failure surfaces as a warning so a degraded scan is never silently reported as clean. |
lui, cdw, templateModernization | BPA cascade → content-scan fallback | When a BPA CSV/CAM source is present, these come from BPA (subtypes custom.classic.widget; legacy.dialog.classic/.coral2; legacy.static.template + custom.static.template). With no BPA source, a heuristic .content.xml scan is the fallback — for templateModernization it walks apps/<appId>/templates/** at any depth (nested/grouped templates included) and classifies each static template as custom.static.template or legacy.static.template from its page-component resource type, so the custom-vs-legacy distinction survives even without a BPA report. Sample prompts route to Branch D (legacy-ui) / Branch C (templates), not code-assessment |
guavaCache | bpa-only (no analyzer, no content-scan) | BPA is the sole source of truth (subtype custom.guava.cache), one finding per bundle — identifier on this subtype is a Guava-internal class, not a customer class, so raw rows are deduped to the bundle named in the message, not surfaced per row. With no BPA source, guavaCache has no deterministic fallback and surfaces under Tier 4 — LLM scan: the agent greps .java files for import com.google.common.cache per module, per references/guava-cache.md, and tags the result confidence: llm. There is deliberately no compiled analyzer detector for this pattern — it does not run inside code-assessment's own discovery. |
dispatcherConversion | content-scan | Heuristic scan for an AMS / on-prem Dispatcher config layout (a monolithic dispatcher.any + conf.vhost.d/, or conf.dispatcher.d/ AMS trees). Detected by dispatcher-inventory.js; the sample prompt routes to Branch E. |
htlLint, osgiConfig, vault-package-dependencies, and the content-scan fallback for lui/cdw/templateModernization are heuristic (tagged confidence: heuristic in the cache) — candidate matches, not compiler-validated. BPA-sourced lui/cdw/templateModernization/replication/guavaCache findings are authoritative. Out of scope: inject-in-sling-model and outdated-dependencies (those belong to code-assessment's own runbook, not migration).
BPA is the source of truth when a report is available. lui/cdw/templateModernization/replication are read from the BPA CSV/CAM (the parser now extracts these subtypes and excludes _COUNT_*/_STAT summary rows), so the runbook counts match your BPA report's LUI-dialog / CDW / static-template / REP tallies. lui keeps only the dialog sub-types (legacy.custom.component → create-component; legacy.static.template is counted under templateModernization). The .content.xml scan is only the fallback when no BPA source is present — and it can undercount relative to BPA when the flagged legacy nodes live in packages (e.g. acs-commons) not in the project source. replication: BPA replication.agent findings when a report is present, else the analyzer detects Replicator usage from source.
The runbook is produced by a single CLI command. It runs the local analyzer + every scanner itself, writes both migration-runbook.md and the sidecar cache, and prints the summary line. You do not need to require() the generator, learn its internals, or write an in-process MCP bridge.
Base (always):
node scripts/runbook-generator.js <workspaceRoot> --out ./migration-runbook.md --cache ./migration-runbook.jsonThis alone covers the local analyzer (Java cascade patterns), html-scan, config-scan, pom-scan, and the content-scan patterns.
If you have a BPA CSV: add --csv ./reports/bpa.csv.
If CAM/MCP is configured: make the one BPA fetch you would make anyway — call the CAM MCP tool for each relevant slug (scheduler, resourceChangeListener, eventListener, eventHandler, assetApi, replication, lui, cdw, templateModernization, guavaCache, urc), write the raw targets to a JSON file keyed by slug, and pass it:
# bpa.json shape: { "<slug>": [ <raw BPA target>, ... ], ... } (a slug you omit = "not in report", treated as clean)
node scripts/runbook-generator.js <workspaceRoot> --bpa-json ./bpa.json --out ./migration-runbook.md --cache ./migration-runbook.jsonThe analyzer is always unioned with BPA — when a BPA source reports a cascade pattern clean but real source code exists, the local analyzer's finding is added anyway (deduped by class name, shown as Detected via: BPA / CAM + analyzer). So you don't reconcile "BPA clean" against the source by hand — the script already does it.
Tier 4 — LLM scan (only if the command prints ⚠️ Needs LLM scan: …). That happens for a cascade/bpa-only pattern nothing deterministic could scan (e.g. guavaCache with no BPA source and no JDK). Grep for it per the pattern guide's hints under {code-assessment}/<pattern>/ (for guavaCache, import com.google.common.cache — see references/guava-cache.md), write the hits to a JSON file, and re-run the same command with --llm-findings:
# llm.json shape: { "<pattern>": [ { "file": "...", "line": 42, "snippet": "..." }, ... ] }
node scripts/runbook-generator.js <workspaceRoot> --bpa-json ./bpa.json --llm-findings ./llm.json --out ./migration-runbook.md --cache ./migration-runbook.jsonThe generator merges, re-renders, and rewrites the cache in that one pass — no hand-editing of internal objects.
After the command finishes, tell the user (numbers come from the printed summary line):
"I've written
migration-runbook.md— {totalFindings} findings across {N} patterns (detected via {sources}). It's read-only. Reply with the pattern you want to migrate first (e.g.scheduler) and I'll reuse the findings already discovered for that pattern — no re-scan needed — and run the one-pattern-per-session apply workflow."
The command also writes the sidecar findings cache (default ./migration-runbook.json) alongside the markdown, holding each pattern's raw findings and their source. Step 3 below reads this cache first before falling back to a live BPA/analyzer/scan lookup.
Skip Step 0 when the user names a specific pattern up front (e.g. "fix scheduler findings", "fix htlLint in ui.apps", "scan my config files for Cloud Manager secrets") — go straight to the relevant apply flow. Step 0 is only for a broad review/scan request with no single pattern named.
Advanced (programmatic). The same behavior is available as an API —
generateRunbook({ workspaceRoot, bpaFilePath, preFetchedBpa, llmByPattern, outputPath, cachePath })andmergeLlmFindings(gathered, llmByPattern)from./scripts/runbook-generator.js— for callers that need to drive it in-process (e.g. an in-sessionmcpFetcherinstead of--bpa-json). Prefer the CLI above; reach for the API only when the CLI can't express what you need.
If the user asks to fix everything or BPA mixes patterns, ask which pattern first. Prefer one commit per pattern session.
First check the non-Java branches (routed in full under Required delegation), which take no BPA pattern id:
/conf templates", "static to editable", "structure/component/policy rewrite rules", "parsys to container", "AEM Modernize Tools") → Branch C.lui → dialog, cdw → cdw).If the request is dispatcher conversion — convert or migrate an AMS or on-premise Dispatcher configuration to AEM as a Cloud Service — follow Branch E. No Java pattern module is needed. Skip Branch B.
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.
Check for a cached runbook first. If ./migration-runbook.json (or the path passed to
generateRunbook's cachePath option) exists and its findingsByPattern[<active pattern>] array
is non-empty, reuse it instead of re-deriving findings:
{ generatedAt, workspaceRoot, sourceByPattern, findingsByPattern } from the cache file.allFindings = findingsByPattern[<active pattern>]. Each entry is
{ pattern, file, line, snippet } — file is relative to the cache's workspaceRoot
(resolve with path.join(workspaceRoot, file)); line/snippet are null when the pattern's
source was mcp/csv (BPA-sourced findings never carry line/snippet; only the analyzer,
html-scan, and config-scan sources do). osgiConfig findings additionally carry a kind
and, for already-placeholdered rows, informational: true — skip those when building the fix
work list.const { paginate } = require('./scripts/unified-collection-reader.js');
const { targets, paging } = paginate(allFindings, { offset, limit: 5 });{ targets, paging } envelope, batch-of-5 default, and
paging.nextOffset semantics as the live getBpaFindings path below — only the data source
differs. Do not bypass the batch-of-5 discipline just because the whole array is already in
memory.<pattern> from the existing runbook (generated at
<generatedAt>)."with_findings (pre-resolved) invocation (see
{code-assessment}/references/runbook.md). Findings with line/snippet already populated skip
the analyzer re-run entirely; findings with only file populated (BPA-sourced) still trigger one
analyze.sh --files <paths> call inside code-assessment to resolve line/snippet before the
edit plan is built.For BPA patterns (scheduler, resourceChangeListener, replication, eventListener, eventHandler, assetApi, lui, cdw) when no usable cache exists: 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.
For dispatcherConversion, targets are the Dispatcher config root(s) and detected mode from the dispatcher-inventory.js scan (surfaced in the Step 0 runbook), not from BPA — go straight to Branch E.
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. If a cached runbook entry already has htlLint findings
(they carry "confidence": "heuristic" — regex matches, not compiler-validated), reuse them per
the cache-first rule above, but re-open and re-confirm each hit against the patterns in
{code-assessment}/references/data-sly-test-redundant-constant.md before editing. If there is no
cache, targets come from proactive rg discovery. See htlLint flow below.
For osgiConfig (OSGi → Cloud Manager): the cache holds heuristic, review-only findings
("confidence": "heuristic", key names + locations only — no secret values). Use them as a
starting checklist, then follow Branch A and references/osgi-cfg-json-cloud-manager.md
to classify each value (real secret? Adobe-owned PID → needs_user_review) — never trust the
heuristic plaintext-secret label without confirming.
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):
After finishing the batch, summarise for this batch only: paging.returned of paging.total processed (with class names), files touched, and any skips/failures. If paging.hasMore, tell the user "Processed batch of N (offset {offset}–{offset + returned − 1} of {total}). Reply continue for the next batch, or name specific classes."; otherwise say the pattern is done and move to the session report.
Then stop and wait — resume only when the user explicitly asks, per the Batched-processing rules.
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, and one batch per call — process, report, stop. Never hold more
than one batch in memory, pre-fetch, or merge across batches. Never pass limit: null in
the skill flow (that option is for programmatic callers wanting the full list).result.paging.nextOffset from the previous call —
read nextOffset, never compute offset + limit yourself.offset: previous.paging.nextOffset; a later session with the same pattern + offset
gets the same batch. Done when paging.hasMore === false (or nextOffset === null).<collectionsDir>/unified-collection.json;
MCP <collectionsDir>/mcp/<projectId>/<pattern>.json.[User] "Fix scheduler findings using ./reports/bpa.csv" (MCP path: pass { mcpFetcher, projectId } instead of bpaFilePath)
[Agent] getBpaFindings('scheduler', { bpaFilePath, limit: 5, offset: 0 })
// first call parses CSV (or fetches MCP once) → writes cache → 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`."
[User] "continue"
[Agent] getBpaFindings('scheduler', { bpaFilePath, limit: 5, offset: 5 }) // reads cache — no re-parse / no new MCP call
→ paging: { ..., offset: 5, nextOffset: 10, hasMore: true }
...Source priority (BPA patterns): unified collection → BPA CSV → MCP → manual paths — not an automatic cascade after MCP errors (if MCP fails, stop; see MCP errors and fallback). Batch size 5 on every BPA source. htlLint, OSGi→Cloud Manager, Template Modernization (C), and Legacy UI (D) do not use BPA/MCP — see their branches in Required delegation.
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© adobe, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 56 other files (scripts, references) in plugins/aem/cloud-service/skills/migration of adobe/skills.
Open the folder on GitHubat commit cbc9952
Migration next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Migration this skilladobe/skills | 195 | — | ~11k | Automated safety check: Pass | Apache-2.0 | |
| Prepare Cloudflare Production DeploymentLubomirGeorgiev/cloudflare-workers-nextjs-saas-template | 786 | — | ~5.9k | Automated safety check: Notes | MIT | |
| DB Ops SopOpenDCAI/DataMind | 406 | — | ~388 | Automated safety check: Pass | Apache-2.0 | |
| RAG Troubleshootlyonzin/knowledge-rag | 290 | — | ~1.8k | Automated safety check: Pass | MIT | |
| Planetscale Best Practices Matrixplanetscale/skills | 132 | — | ~1.4k | Automated safety check: Pass | MIT | |
| Codflow Setupbighadj22/codflow | 343 | — | ~7.8k | Automated safety check: Notes | Apache-2.0 |
LubomirGeorgiev/cloudflare-workers-nextjs-saas-template
Source-of-truth runbook for preparing this Vinext Cloudflare Workers SaaS template for production deployment.
OpenDCAI/DataMind
Database operations runbook — backup, recovery, performance tuning, troubleshooting.
lyonzin/knowledge-rag
When the user reports a bug, error message, stack trace, unexpected behavior, or "why is this broken" question, search the corpus first for prior occurrences, known fixes, or related runbooks.
planetscale/skills
A concise feature matrix for deciding which PlanetScale safety, observability, and automation recommendations apply by engine.
bighadj22/codflow
Setup runbook for CodFlow — an AI agent following it authenticates with Cloudflare, creates the required resources (D1, R2, KV) in the developer's account, binds their real IDs into both…
lyonzin/knowledge-rag
For any security-related task — threat triage, incident response, MITRE ATT&CK mapping, CVE lookup, exploit analysis, defensive control validation, red/blue/purple team work — always consult the…
adobe/skills
Scaffolds, implements, deploys and debugs Adobe Runtime actions in App Builder projects, with templates for webhooks, events, database CRUD, sequences and Asset Compute workers.
adobe/skills
Launches Chrome with an unpacked extension over CDP, opens its sidepanel, popup or options page, and hands over to cdp-connect for clicks, typing and screenshots.
adobe/skills
Extracts icons, metadata, text, forms, videos and social links from any web page with playwright-cli, with SVG icon classification and cleanup.
adobe/skills
Detect all languages used on a webpage — both declared (html@lang, hreflang alternate links, nested lang= attributes, meta content-language) and actually present in the body text (Google CLD3 via…
adobe/skills
Prepare any webpage for clean interaction by detecting and removing disruptive overlays (cookie banners, GDPR consent, modals, popups, newsletter signups, paywalls, login walls).
adobe/skills
Reduce a webpage to a structural skeleton with semantic tokens.
Categories
Migrates legacy AEM (6.x, AMS, on-prem) to AEM as a Cloud Service via BPA CSV/cache and CAM/MCP discovery, one pattern per session. Migration is an agent skill from adobe/skills.x, AMS, on-prem) to AEM as a Cloud Service via BPA CSV/cache and CAM/MCP discovery, one pattern per session.
Migration fits situations like: review/scan a project for AEMaaCS migration — generates a read-only migration-runbook.md —; fix specific Cloud Service blockers: scheduler; resourceChangeListener; OSGi EventHandler.
Run `npx skills add adobe/skills --skill migration -a claude-code`. Or copy the skill folder (plugins/aem/cloud-service/skills/migration in adobe/skills) into .claude/skills/migration in your project. Claude Code loads it when a task matches its description.
Run `npx skills add adobe/skills --skill migration -a codex`. Or copy the skill folder (plugins/aem/cloud-service/skills/migration in adobe/skills) into .agents/skills/migration in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add adobe/skills --skill migration -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/migration, .gemini/skills/migration, .github/skills/migration and .opencode/skills/migration in your project.
Going by SKILL.md and its folder, Migration needs the command-line tools its instructions call (node and mvn).
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.
Migration is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 11k tokens (SKILL.md is roughly 46k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 56k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Migration: Prepare Cloudflare Production Deployment (LubomirGeorgiev/cloudflare-workers-nextjs-saas-template, 786 stars), DB Ops Sop (OpenDCAI/DataMind, 406 stars), RAG Troubleshoot (lyonzin/knowledge-rag, 290 stars) and Planetscale Best Practices Matrix (planetscale/skills, 132 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
adobe (a GitHub organization) maintains it in adobe/skills, which has 195 GitHub stars. The repository holds 105 skills in this directory. The repository was last updated on October 6, 2026.
Source: adobe/skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.