All AEM as a Cloud Service skills: component development, Workflow model design, development, triggering, launchers, debugging, and triaging, Dispatcher config authoring, advisory, incident response, performance tuning, and security hardening, Rapid Development Environment (RDE) deployment, log inspection, snapshots, and troubleshooting via `aio aem rde`, Jackrabbit Oak query/index tuning (property, Lucene index definitions), and extracting/inventorying JCR queries (XPath, SQL2, QueryBuilder) across a codebase.
85%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Low
Low-risk findings worth noting
Agent: The parent skill loads this file for prompts such as "scan my config files and create Cloud Manager environment secrets or variables." Users do not name this path.
The sections below through Cloud Manager and deployment reproduce the rules from Adobe Experience Manager as a Cloud Service product documentation for configuring OSGi (deploying topic: OSGi configuration with secret and environment-specific values). Follow them when editing configs or advising on Cloud Manager.
OSGi manages bundles and their configurations. Settings are defined in configuration files that are part of the AEM code project. Cloud Manager is used to configure environment variables that back OSGi placeholders.
.cfg.json)ui.config) as .cfg.json files under runmode-specific config folders, for example under paths like /apps/<appId>/config.<runmode>/ (in the content tree; in Maven this is commonly under ui.config / osgiconfig)..cfg.json format defined by the Apache Sling OSGi configuration installer..../config/com.example.workflow.impl.ApprovalWorkflow.cfg.json<factoryPID>-<name>.cfg.json naming convention.Superseded formats: Older AEM versions allowed .cfg, .config, and XML sling:OsgiConfig. On AEM as a Cloud Service these are superseded by .cfg.json. See Phase 0 — Legacy format conversion below for conversion rules.
Cloud runtime note: On AEM as a Cloud Service, effective OSGi configuration is not held like a classic on-prem /apps-only model; use the environment's Developer Console (Status → Configurations in the status dump) to inspect what is applied.
$[env:…] / $[secret:…])./apps/<appId>/ using names like config.<author|publish>.<dev|stage|prod> (and combinations such as config.author, config.author.dev). Configs apply when the folder's runmodes match the instance.config.preview folder is not declared like config.publish. The preview tier inherits OSGi configuration from publish.-r publish,dev on the quickstart JAR.Valid Cloud Service runmode tokens (exhaustive):
| Token | Meaning |
|---|---|
author | Author tier |
publish | Publish tier |
dev | Development environment |
stage | Stage environment |
prod | Production environment |
Valid folder names use combinations: config, config.author, config.publish, config.dev, config.stage, config.prod, config.author.dev, config.author.stage, config.author.prod, config.publish.dev, config.publish.stage, config.publish.prod. Any folder whose name does not match one of these (e.g. config.qa, config.integration, config.local, config.ams) is invalid on Cloud Service.
Verifying effective config: In Cloud Service, use Developer Console → select Pod → Status → Status Dump → Configurations → Get Status. Match pid to the .cfg.json filename and compare properties to the repo for the runmode under review.
Three kinds (a single .cfg.json may mix them):
{ "connection.timeout": 1000 }{ "api-key": "$[secret:server-api-key]" }dev runmode), e.g.{ "url": "$[env:server-url]" }Example combining all three:
{
"connection.timeout": 1000,
"api-key": "$[secret:server-api-key]",
"url": "$[env:server-url]"
}$[env:ENV_VAR_NAME] (non-secret) — Use only when values differ for preview vs publish or across development environments (including local SDK and Cloud dev). For Stage and Production, avoid non-secret $[env:…] except where preview must differ from publish; use inline values in config.stage / config.prod for non-secrets. Do not use $[env:…] to push routine runtime changes to Stage/Prod without source control.
$[secret:SECRET_VAR_NAME] — Required for any secret OSGi value (passwords, private API keys, anything that must not be in Git). Use for all Cloud environments including Stage and Production.
$[env:…] must be used only for OSGi properties related to customer custom code. It must not be used to override Adobe-defined OSGi configuration. Treat $[secret:…] the same way for this skill: do not introduce placeholders on Adobe/product PIDs unless the user explicitly confirms an allowed exception.
Placeholders cannot be used in repoinit statements. Do not add $[secret:…] or $[env:…] to Repository Initializer content. Skip org.apache.sling.jcr.repoinit.RepositoryInitializer* files for placeholder injection.
$[env:ENV_VAR_NAME]$[secret:SECRET_VAR_NAME]If no value is set in Cloud Manager, the placeholder may remain unreplaced. Default (for both env and secret placeholders):
$[env:ENV_VAR_NAME;default=<value>](with the same pattern for secrets when a default is appropriate per product behavior).
Applies to both $[env:…] and $[secret:…] variable names:
| Rule | Requirement |
|---|---|
| Name length | 2–100 characters |
| Name pattern | [a-zA-Z_][a-zA-Z_0-9]* |
| Value length | Values must not exceed 2048 characters |
| Count | Up to 200 variables per environment |
Reserved prefixes: Names starting with INTERNAL_, ADOBE_, or CONST_ are reserved—customer variables with those prefixes are ignored. Customers must not reference INTERNAL_ or ADOBE_ variables.
AEM_: Variables with prefix AEM_ are product-defined public API. Customers may use and set those Adobe provides but must not define new custom variables with the AEM_ prefix.
$[env:…]: Define normal process environment variables before starting AEM (e.g. export ENV_VAR_NAME=my_value). A small shell script run before startup is recommended; non-secret values may be shared in source control if appropriate.$[secret:…]: Each secret needs a plain text file named exactly after the variable (e.g. for $[secret:server_password] a file server_password). No file extension. Store all such files in one directory and set Sling org.apache.felix.configadmin.plugin.interpolation.secretsdir to that directory in crx-quickstart/conf/sling.properties (framework property, not Felix web console), e.g.org.apache.felix.configadmin.plugin.interpolation.secretsdir=${sling.home}/secretsdirUse separate config.author and config.publish folders. Prefer the same variable name in both with $[env:ENV_VAR_NAME;default=<value> where the default matches the tier, and bind values per tier in Cloud Manager using the API service parameter (author, publish, or preview). Alternatively use distinct names such as author_<name> and publish_<name>.
PATCH /program/{programId}/environment/{environmentId}/variables — deploys variables like a pipeline deploy; author and publish restart and pick up values after a few minutes. Body is a JSON array of objects with name, value, and type: use string (default) for $[env:…], secretString for $[secret:…].;default=…).GET the same …/variables path. Delete: PATCH with the variable included and an empty value.aio cloudmanager:list-environment-variables ENVIRONMENT_IDaio cloudmanager:set-environment-variables ENVIRONMENT_ID --variable NAME "value" --secret NAME "value"aio cloudmanager:set-environment-variables ENVIRONMENT_ID --delete NAME …Environment variables can also be maintained in the Cloud Manager UI (Environment variables).
Secret and env-specific values live outside Git. Customers should govern them as part of the release process. Variable API calls do not run the same quality gates as a full code pipeline. Set variables before or when deploying code that depends on them. The API may fail while a pipeline is running; errors may be non-specific.
Additive changes: Prefer new variable names when rotating values so older deployments never pick up wrong values; remove old names only after releases are stable. This also helps rollbacks and disaster recovery when redeploying older code.
In scope for automated edits
.cfg.json files under ui.config or ui.apps/.../jcr_root/... where the file's parent directory name starts with config..cfg, .config, and XML sling:OsgiConfig nodes — conversion to .cfg.json only (Phase 0).Out of scope for automated edits
config.* / install.* folders are flagged as URC, not moved or renamed).Glob for .cfg, .config, and .xml files under config folders (same scope as .cfg.json). For each legacy file found, convert it to .cfg.json and delete the original.
.cfg (Java properties format)
Line-based key=value (or key = value). Lines starting with # or ! are comments. Multi-line values use trailing \. Convert:
# .cfg file
server.url=https://example.com
connection.timeout=1000
enabled=true→
{
"server.url": "https://example.com",
"connection.timeout": 1000,
"enabled": true
}Rules: unquoted numeric values → JSON numbers; true/false → JSON booleans; everything else → JSON strings. Preserve the PID from the filename.
.config (Apache Felix format)
Similar to .cfg but supports typed values with suffixes: I (int), L (long), F (float), D (double), B (byte), S (short), C (char). Arrays use square brackets [ "a", "b" ]. Booleans are B"true" or unquoted true/false. Convert types to their JSON equivalents (numbers, booleans, arrays). Drop type suffixes.
XML sling:OsgiConfig nodes
JCR content XML (.content.xml) with jcr:primaryType="sling:OsgiConfig". Each @property attribute is a config property. Type hints in curly braces: {Long}, {Boolean}, etc. Arrays use []. Convert to JSON, derive PID from the node name, write as <PID>.cfg.json. Do not delete the .content.xml if it contains non-OSGi nodes — only remove the sling:OsgiConfig node from it.
After conversion: Proceed with the converted .cfg.json files in Phase 1+. Mention converted files in the report.
This phase flags issues for user review. Do not auto-delete or auto-merge — list findings in the handoff file under a "cleanup" array.
1a. Unsupported run modes (URC)
Adobe's Pattern Detector reports these as URC (Unsupported Run modes Configuration; BPA subtype unsupported.runmode, importance CRITICAL). Configurations based on unsupported run mode names have no effect when deployed to AEM as a Cloud Service.
The migration runbook sources URC BPA-report-first (from the CAM/MCP fetch or a local BPA CSV); when the report actually contains URC findings it is authoritative. When a BPA source is present but reports no URC findings, the skill also runs local folder detection as a safety net (see below). Local detection covers both config.<runmode> and bundle install.<runmode> folders.
A folder is flagged when its run mode is unsupported. Beyond unknown tokens (e.g. config.qa, config.preprod, install.local), note the ordering rule: the tier token must precede the environment token. config.author.dev is valid; config.dev.author is not — even though dev and author are individually valid. A folder may carry at most one tier and one environment token, and config.preview cannot be declared (preview inherits from publish). Run mode tokens are case-sensitive and lowercase-only — config.Author.dev or config.PUBLISH are themselves unsupported run modes, not case variants of a valid one.
A BPA report that reports no URC findings does not by itself prove the project is URC-clean — the report may predate URC detection or be scoped to other patterns. The skill treats BPA as authoritative only when it actually reports URC findings; otherwise it warns and runs the local config.*/install.* safety-net scan.
Report (flag-only): folder path, the offending run mode, and remediation — evaluate whether the configuration is needed → rename to a supported run mode identifier following the run-mode resolution rules → remove if obsolete. See the aem-guides-wknd-legacy code/urc branch for corrected examples. Sources: URC pattern, Configuring OSGi.
URC auto-fix (apply step). Ordering-only violations — every token a valid
tier/env in the wrong order, e.g. config.dev.author → config.author.dev (and
install.<env>.<tier> likewise) — are deterministically fixable by
reordering to <prefix>.<tier>.<env>. Discovery/runbook only flags them; the fix
happens during apply (the same phase that runs Phase 0 conversions):
planRunmodeReorders(workspaceRoot) (read-only) to get the safe reorder
set and the manual list.git mv "<from>" "<to>" itself
(preserve history; fall back to a plain move if the path isn't git-tracked),
and records the move under a runmode_reorders array in the handoff file.Never auto-applied (routed to the handoff cleanup array for a human
decision): unknown-token folders (config.preprod, config.qa, install.local),
duplicate tier/env folders (config.author.publish), and any reorder whose
target folder already exists (renaming would change PID resolution — merge
manually).
1b. Archetype / boilerplate configs
Flag .cfg.json files whose PID matches the list below — these are commonly generated by the AEM archetype and are often unnecessary or need review before deploying to Cloud Service:
| PID | Why flag |
|---|---|
org.apache.sling.commons.log.LogManager.factory.config* | Custom logger factories — review log levels; Cloud Service uses centralized logging |
org.apache.sling.jcr.davex.impl.servlets.SlingDavExServlet | WebDAV — disabled on Cloud Service |
org.apache.sling.servlets.get.DefaultGetServlet | Default GET servlet — usually archetype boilerplate |
com.day.cq.wcm.core.impl.AuthoringUIMode | Touch UI default — unnecessary on Cloud Service |
com.adobe.granite.auth.saml.SamlAuthenticationHandler.factory* | SAML — must be validated against Cloud Service IDP setup |
org.apache.sling.security.impl.ReferrerFilter | Referrer filter — Cloud Service has its own; review carefully |
org.apache.sling.engine.impl.SlingMainServlet | Sling main servlet — rarely needs override on Cloud Service |
Action: flag with "needs_user_review": true and reason. Do not delete.
1c. Duplicate configs across runmode folders
For each PID, if the exact same JSON content appears in multiple runmode folders (e.g. config.author.dev/ and config.author.stage/ have identical files), flag as a consolidation candidate. Suggest moving to the least-specific common parent folder (e.g. config.author/). Do not auto-merge.
1d. Environment-specific URLs
Scan .cfg.json string values for patterns that look like environment-specific URLs: values matching https?:// that contain hostname fragments suggesting a specific environment (substrings: dev, stage, stg, qa, uat, prod, preprod, localhost, local, or IP addresses like 10.*, 192.168.*). Flag each with the property key, current value, and "needs_user_review": true, "reason": "Possible environment-specific URL — consider $[env:VAR_NAME]". Do not auto-replace — URL classification requires human judgment.
1e. Deprecated / Cloud Service–incompatible configs
Flag .cfg.json files whose PID matches known deprecated or incompatible services:
| PID pattern | Reason |
|---|---|
com.day.cq.replication.impl.AgentManagerImpl | Replication agents — Cloud Service uses Sling content distribution |
com.day.cq.replication.impl.TransportHandler* | Transport handlers — not applicable on Cloud Service |
org.apache.sling.jcr.webdav.impl.servlets.SimpleWebDavServlet | WebDAV servlet — disabled on Cloud Service |
com.day.cq.mailer.DefaultMailService | Mail service — review; Cloud Service uses different mail configuration |
org.apache.felix.http.jetty* | Jetty tuning — managed by Adobe on Cloud Service |
Flag with reason. Do not delete.
This is the original core workflow, now applied after Phase 0 and Phase 1.
.cfg.json (including newly converted ones from Phase 0)."$[secret:VAR]"; replace eligible non-secrets with "$[env:VAR]" only when Adobe's rules above allow. Skip ambiguous or Adobe-owned configs → needs_user_review in the handoff file.Write gitignored cloudmanager-osgi-secrets.local.json at the AEM repo root. Structure:
{
"_do_not_commit": "DELETE this file after applying variables in Cloud Manager",
"variables": [
{
"name": "VAR_NAME",
"value": "<original value>",
"cm_type": "secretString | string",
"placeholder": "$[secret:VAR_NAME]",
"cfg_json_path": "relative/path/to/file.cfg.json",
"json_property": "property.key",
"needs_user_review": false
}
],
"cleanup": [
{
"type": "invalid_runmode | unsupported_runmode | archetype_default | duplicate_config | env_specific_url | deprecated_config",
"path": "relative/path",
"detail": "human-readable description",
"suggestion": "what to do"
}
],
"conversions": [
{
"original": "relative/path/to/legacy.config",
"converted": "relative/path/to/PID.cfg.json"
}
],
"runmode_reorders": [
{
"from": "ui.config/.../config.dev.author",
"to": "ui.config/.../config.author.dev",
"command": "git mv \"ui.config/.../config.dev.author\" \"ui.config/.../config.author.dev\""
}
]
}.gitignore entry if missing.Secrets: Treat as secret candidates when keys suggest sensitivity (password, secret, apikey, token, clientsecret, credential, privatekey, etc.) or values are obviously secret; exclude hostnames, public IDs, and non-secret flags unless keys indicate otherwise.
Env-specific URLs: Match https?:// values against hostname fragments (dev, stage, stg, qa, uat, prod, preprod, localhost, local, or private IPs). Flag only — do not auto-replace.
Archetype / deprecated PIDs: Match filenames (minus .cfg.json) against the PID tables in Phases 1b and 1e. Use prefix matching for factory configs (PID ending in * in the tables).
Phase 0: convert legacy formats → Phase 1: flag URC (unsupported run modes, BPA-first / local fallback over config.* + install.*), archetype defaults, duplicates, env-specific URLs, deprecated configs → Phase 2: inject $[secret:] / $[env:] placeholders on custom PIDs → Phase 3: gitignored handoff file with variables + cleanup items → no secrets in chat.
.tessl-plugin
skills
aem-rde
aem-workflow
code-assessment
content-distribution
create-component
assets
references
dispatcher
incident-response
references
dispatcher-foundation
performance-tuning
references
dispatcher-foundation
security-hardening
references
dispatcher-foundation
technical-advisory
references
dispatcher-foundation
workflow-orchestrator
references
dispatcher-foundation
ensure-agents-md
references
migration
references
dispatcher
legacy-ui
template-modernization
validation
scripts
dispatcher-tool
validate-migration