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
/conf Editable TemplateGenerates the 4-node editable template under ui.content/.../jcr_root/conf/<appId>/settings/wcm/templates/<templateName>/ (root .content.xml, structure/, initial/, policies/) and appends the name to templates/.content.xml. Runs only for plan rows where Create editable? is true; every input is sourced from the confirmed .migration/template-context.yml — see template-modernization-context.md. If any field is needs-user-confirm / missing, stop.
Placeholders below: <appId>, <templateName>, <humanTitle>, <allowedPathsPattern>, <pageStructureResourceType>, <contentContainerResourceType> map to the obvious context fields; breakpoints come from conventions.breakpoints verbatim.
.content.xmlPath:
ui.content/src/main/content/jcr_root/conf/<appId>/settings/wcm/templates/<templateName>/.content.xmlFormat:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0"
jcr:primaryType="cq:Template"
allowedPaths="[<allowedPathsPattern>]">
<jcr:content
cq:templateType="/conf/<appId>/settings/wcm/template-types/page"
jcr:primaryType="cq:PageContent"
jcr:title="<humanTitle>"
status="enabled"/>
</jcr:root>Field mapping:
| Field | Source |
|---|---|
allowedPaths | Copied from static template's allowedPaths, or derived from content path pattern |
cq:templateType | Always the discovered template-types path (/conf/<appId>/settings/wcm/template-types/page) |
jcr:title | Copied from static template's jcr:content/jcr:title |
status | Always "enabled" for active templates |
Do not include cq:lastModified or cq:lastModifiedBy — these are author-set timestamps, not source-controlled.
structure/.content.xmlThe structure defines the fixed page layout authors see in the template editor. It mirrors the static template's page component structure.
Path:
ui.content/src/main/content/jcr_root/conf/<appId>/settings/wcm/templates/<templateName>/structure/.content.xmlFormat:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
xmlns:cq="http://www.day.com/jcr/cq/1.0"
xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
jcr:primaryType="cq:Page">
<jcr:content
cq:deviceGroups="[/etc/mobile/groups/responsive]"
cq:template="/conf/<appId>/settings/wcm/templates/<templateName>"
jcr:primaryType="cq:PageContent"
sling:resourceType="<appId>/components/structure/<templateName>">
<root
jcr:primaryType="nt:unstructured"
sling:resourceType="wcm/foundation/components/responsivegrid">
<responsivegrid
jcr:primaryType="nt:unstructured"
sling:resourceType="<appId>/components/content/container"
editable="{Boolean}true"
layout="responsiveGrid"/>
</root>
<cq:responsive jcr:primaryType="nt:unstructured">
<breakpoints jcr:primaryType="nt:unstructured">
<!-- One node per conventions.breakpoints entry. Node name, title and
width all come from context — do not hardcode names or numbers.
Example shape for an entry { name, title, width }: -->
<BREAKPOINT_NAME
jcr:primaryType="nt:unstructured"
title="<breakpointTitle>"
width="{Long}<breakpointWidth>"/>
</breakpoints>
</cq:responsive>
</jcr:content>
</jcr:root>Key rules:
cq:template points back to the template's own path (self-reference)sling:resourceType on jcr:content = <appId>/components/structure/<templateName> — must match the page structure component discovered in step 3<root> uses wcm/foundation/components/responsivegrid — the WCM layout container<responsivegrid> inside root uses the app's own content container (<appId>/components/content/container) with editable="{Boolean}true" — this marks it as the author-editable zone<cq:responsive> breakpoints: emit one node per entry in conventions.breakpoints (name, width, title verbatim). Never invent values — if conventions.breakpoints is needs-user-confirm, stop and ask the user. There is no numeric default.cq:deviceGroups="[/etc/mobile/groups/responsive]" — always include on structure. This is the standard AEM responsive device-group path used by the template editor; use it verbatim. Do not invent a per-project value, and do not "modernize" it to a /conf or /libs path — the template editor still resolves device groups from /etc/mobile/groups.For each namedChildren entry with placement ∈ {structure, structure+initial}, emit a child of <root>:
| classification | shape |
|---|---|
parsys | <name jcr:primaryType="nt:unstructured" sling:resourceType="<contentContainerResourceType>" editable="{Boolean}true" layout="responsiveGrid"/> |
locked | <name jcr:primaryType="nt:unstructured" sling:resourceType="<entry.resourceType>"/> — no editable |
Never emit anything in structure/ that isn't classified with one of those placements — the context classifier (step 5) is authoritative. Placing a locked child in initial/ would delete it from pre-existing pages on the next template update.
initial/.content.xmlThe initial content is copied into every new page created from this template. It should be structurally identical to structure but without editable, cq:deviceGroups, and <cq:responsive>.
Path:
ui.content/src/main/content/jcr_root/conf/<appId>/settings/wcm/templates/<templateName>/initial/.content.xmlFormat:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
xmlns:cq="http://www.day.com/jcr/cq/1.0"
xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
jcr:primaryType="cq:Page">
<jcr:content
cq:template="/conf/<appId>/settings/wcm/templates/<templateName>"
jcr:primaryType="cq:PageContent"
sling:resourceType="<appId>/components/structure/<templateName>">
<root
jcr:primaryType="nt:unstructured"
sling:resourceType="wcm/foundation/components/responsivegrid">
<responsivegrid
jcr:primaryType="nt:unstructured"
sling:resourceType="<appId>/components/content/container"
layout="responsiveGrid"/>
</root>
</jcr:content>
</jcr:root>Differences from structure:
cq:deviceGroupseditable="{Boolean}true" on any child node<cq:responsive> breakpoints blockPre-placed components:
For templates where new pages must start with a specific component already present (e.g. a single-artifact template where the primary component is always required), add the component as a child of <responsivegrid> in initial content:
<responsivegrid jcr:primaryType="nt:unstructured"
sling:resourceType="<appId>/components/content/container"
layout="responsiveGrid">
<primarycomponent jcr:primaryType="nt:unstructured"
sling:resourceType="<appId>/components/content/<componentName>"/>
</responsivegrid>Only pre-place components that belong in the editable zone. Components locked in structure must not be duplicated here.
Required runtime nodes (e.g. targeting):
For each namedChildren entry the context classified as required-runtime (placement: initial) — a named child the page structure component renders via data-sly-resource that is not a parsys — add it as a direct child of jcr:content in initial, not inside <root>:
<jcr:content ...>
<targeting jcr:primaryType="nt:unstructured"
sling:resourceType="<appId>/components/content/<targetingComponent>"/>
<root ...>
...
</root>
</jcr:content>policies/.content.xmlThe policies node maps each container in the template to a content policy. For new templates this is a minimal placeholder — actual policy assignments are done after deployment via the template editor or can be copied from an existing template.
Path:
ui.content/src/main/content/jcr_root/conf/<appId>/settings/wcm/templates/<templateName>/policies/.content.xmlFormat:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
xmlns:cq="http://www.day.com/jcr/cq/1.0"
xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
jcr:primaryType="cq:Page">
<jcr:content
jcr:primaryType="nt:unstructured"
sling:resourceType="wcm/core/components/policies/mappings">
<root
jcr:primaryType="nt:unstructured"
sling:resourceType="wcm/core/components/policies/mapping">
<responsivegrid
jcr:primaryType="nt:unstructured"
sling:resourceType="wcm/core/components/policies/mapping"/>
</root>
</jcr:content>
</jcr:root>If copying an existing policy reference: If the user has an existing template with a cq:policy value (e.g. wcm/foundation/components/responsivegrid/policy_31297318674600) on the <root> node, that policy can be referenced here. Only copy it if the user explicitly asks to inherit the same policy — otherwise leave the mapping empty (no cq:policy attribute).
templates/.content.xmlThe parent templates/ folder node lists all template names as empty child elements. Read the existing file first and add only the new template names.
Path:
ui.content/src/main/content/jcr_root/conf/<appId>/settings/wcm/templates/.content.xmlFormat (existing entries preserved, new names appended):
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:rep="internal"
xmlns:cq="http://www.day.com/jcr/cq/1.0"
jcr:mixinTypes="[rep:AccessControllable]"
jcr:primaryType="cq:Page">
<rep:policy/>
<existing-template-1/>
<existing-template-2/>
<new-template-name/>
</jcr:root>Rule: Preserve every existing <childName/> entry. Only append new ones. Do not remove or reorder existing entries.
ui.contentConfirm the filter.xml for ui.content includes the templates subtree:
<filter root="/conf/<appId>/settings/wcm/templates"/>If missing, add it. Also verify the template-types path is filtered if it was just created:
<filter root="/conf/<appId>/settings/wcm/template-types"/>Post-generation: run sections 1 and 6 of template-modernization-validation.md. Do not commit on failure.
Once editable templates are created, run aem-modernization.md to generate the structure rewrite rules that reference them. The structure rewrite rule's editableTemplate property must exactly match the path used in cq:template in the template's .content.xml.
The complete migration sequence is:
1. [context] Confirm .migration/template-context.yml + plan table
2. [this generator] Create editable templates → ui.content/.../conf/.../templates/
3. [aem-modernization.md] Create structure/component/policy rewrite rules → ui.apps + ui.config
4. [validation] Run template-modernization-validation.md
5. [manual] Deploy both packages (or via Cloud Manager pipeline)
6. [manual] Allow the template on its parent content tree — add it to the
parent page's `cq:allowedTemplates` (or the relevant policy).
Creating the editable template does NOT make it selectable in
the Create Page wizard; without this step authors won't see it.
7. [manual] Run AEM Modernize Tools UI jobs against content pathsNote (
allowedTemplates): an editable template's ownallowedPathsregex constrains where it may be used, but the template only appears in the Create Page wizard when the target section also allows it (viacq:allowedTemplateson the parentjcr:content, or an Allowed Templates policy). This skill does not edit content trees — surface it as a manual step.
Editable templates created:
<templateName> → conf/<appId>/settings/wcm/templates/<templateName>/
.content.xml (cq:Template root, status=enabled)
structure/ (page layout with responsive grid)
initial/ (initial page content)
policies/ (policy mapping placeholder)
templates/.content.xml updated — added: <list of new template names>
filter.xml <added entry / already present>
Skipped (already exist):
<list of templates that were found and not overwritten>
Review required:
<list any templates where structure component was not found>
<list any templates where allowedPaths could not be determined>needs-user-confirm / missing, stopsling:resourceType on jcr:content must match the actual structure component path — wrong value causes blank page renderingeditable="{Boolean}true" is required on the editable container in structure — without it, authors cannot edit the zone in the template editoreditable to initial — it belongs only on structurecq:template is a self-reference — it points to the template's own /conf path, not to the static templatetemplate-types/page must already exist in the project; ask the user if it is missingtemplates/.content.xml — read it first, add new entries only, never delete existing onesPost-generation: run sections 1 and 6 of template-modernization-validation.md. Do not commit on failure.
.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